import {
processDefineProps,
DEFINE_PROPS,
- WITH_DEFAULTS,
- extractRuntimeProps
+ WITH_DEFAULTS
} from './script/defineProps'
import {
resolveObjectKey,
}
// metadata that needs to be returned
- const bindingMetadata: BindingMetadata = {}
+ // const ctx.bindingMetadata: BindingMetadata = {}
const userImports: Record<string, ImportBinding> = Object.create(null)
const scriptBindings: Record<string, BindingTypes> = Object.create(null)
const setupBindings: Record<string, BindingTypes> = Object.create(null)
}
// 4. extract runtime props/emits code from setup context type
- extractRuntimeProps(ctx)
if (emitsTypeDecl) {
extractRuntimeEmits(emitsTypeDecl, typeDeclaredEmits, error)
}
// 7. analyze binding metadata
if (scriptAst) {
- Object.assign(bindingMetadata, analyzeScriptBindings(scriptAst.body))
+ Object.assign(ctx.bindingMetadata, analyzeScriptBindings(scriptAst.body))
}
if (ctx.propsRuntimeDecl) {
for (const key of getObjectOrArrayExpressionKeys(ctx.propsRuntimeDecl)) {
- bindingMetadata[key] = BindingTypes.PROPS
+ ctx.bindingMetadata[key] = BindingTypes.PROPS
}
}
- for (const key in ctx.typeDeclaredProps) {
- bindingMetadata[key] = BindingTypes.PROPS
- }
for (const key in ctx.modelDecls) {
- bindingMetadata[key] = BindingTypes.PROPS
+ ctx.bindingMetadata[key] = BindingTypes.PROPS
}
// props aliases
if (ctx.propsDestructureDecl) {
if (ctx.propsDestructureRestId) {
- bindingMetadata[ctx.propsDestructureRestId] =
+ ctx.bindingMetadata[ctx.propsDestructureRestId] =
BindingTypes.SETUP_REACTIVE_CONST
}
for (const key in ctx.propsDestructuredBindings) {
const { local } = ctx.propsDestructuredBindings[key]
if (local !== key) {
- bindingMetadata[local] = BindingTypes.PROPS_ALIASED
- ;(bindingMetadata.__propsAliases ||
- (bindingMetadata.__propsAliases = {}))[local] = key
+ ctx.bindingMetadata[local] = BindingTypes.PROPS_ALIASED
+ ;(ctx.bindingMetadata.__propsAliases ||
+ (ctx.bindingMetadata.__propsAliases = {}))[local] = key
}
}
}
userImports
)) {
if (isType) continue
- bindingMetadata[key] =
+ ctx.bindingMetadata[key] =
imported === '*' ||
(imported === 'default' && source.endsWith('.vue')) ||
source === 'vue'
: BindingTypes.SETUP_MAYBE_REF
}
for (const key in scriptBindings) {
- bindingMetadata[key] = scriptBindings[key]
+ ctx.bindingMetadata[key] = scriptBindings[key]
}
for (const key in setupBindings) {
- bindingMetadata[key] = setupBindings[key]
+ ctx.bindingMetadata[key] = setupBindings[key]
}
// known ref bindings
if (refBindings) {
for (const key of refBindings) {
- bindingMetadata[key] = BindingTypes.SETUP_REF
+ ctx.bindingMetadata[key] = BindingTypes.SETUP_REF
}
}
ctx.helperImports.add('unref')
s.prependLeft(
startOffset,
- `\n${genCssVarsCode(cssVars, bindingMetadata, scopeId, isProd)}\n`
+ `\n${genCssVarsCode(cssVars, ctx.bindingMetadata, scopeId, isProd)}\n`
)
}
// generate getter for import bindings
// skip vue imports since we know they will never change
returned += `get ${key}() { return ${key} }, `
- } else if (bindingMetadata[key] === BindingTypes.SETUP_LET) {
+ } else if (ctx.bindingMetadata[key] === BindingTypes.SETUP_LET) {
// local let binding, also add setter
const setArg = key === 'v' ? `_v` : `v`
returned +=
options.templateOptions.compilerOptions),
inline: true,
isTS,
- bindingMetadata
+ bindingMetadata: ctx.bindingMetadata
}
})
if (tips.length) {
return {
...scriptSetup,
- bindings: bindingMetadata,
+ bindings: ctx.bindingMetadata,
imports: userImports,
content: s.toString(),
map: genSourceMap
ObjectExpression,
Expression
} from '@babel/types'
-import { isFunctionType } from '@vue/compiler-dom'
+import { BindingTypes, isFunctionType } from '@vue/compiler-dom'
import { ScriptCompileContext } from './context'
import { inferRuntimeType, resolveQualifiedType } from './resolveType'
import {
unwrapTSNode,
toRuntimeTypeString
} from './utils'
-import { genModels } from './defineModel'
+import { genModelProps } from './defineModel'
export const DEFINE_PROPS = 'defineProps'
export const WITH_DEFAULTS = 'withDefaults'
return true
}
-export function extractRuntimeProps(ctx: ScriptCompileContext) {
- const node = ctx.propsTypeDecl
- if (!node) return
- const members = node.type === 'TSTypeLiteral' ? node.members : node.body
- for (const m of members) {
- if (
- (m.type === 'TSPropertySignature' || m.type === 'TSMethodSignature') &&
- m.key.type === 'Identifier'
- ) {
- let type: string[] | undefined
- let skipCheck = false
- if (m.type === 'TSMethodSignature') {
- type = ['Function']
- } else if (m.typeAnnotation) {
- type = inferRuntimeType(
- m.typeAnnotation.typeAnnotation,
- ctx.declaredTypes
- )
- // skip check for result containing unknown types
- if (type.includes(UNKNOWN_TYPE)) {
- if (type.includes('Boolean') || type.includes('Function')) {
- type = type.filter(t => t !== UNKNOWN_TYPE)
- skipCheck = true
- } else {
- type = ['null']
- }
- }
- }
- ctx.typeDeclaredProps[m.key.name] = {
- key: m.key.name,
- required: !m.optional,
- type: type || [`null`],
- skipCheck
- }
- }
- }
-}
-
export function genRuntimeProps(ctx: ScriptCompileContext): string | undefined {
let propsDecls: undefined | string
if (ctx.propsRuntimeDecl) {
}
}
} else if (ctx.propsTypeDecl) {
- propsDecls = genPropsFromTS(ctx)
+ propsDecls = genRuntimePropsFromTypes(ctx)
}
- const modelsDecls = genModels(ctx)
+ const modelsDecls = genModelProps(ctx)
if (propsDecls && modelsDecls) {
return `${ctx.helper('mergeModels')}(${propsDecls}, ${modelsDecls})`
}
}
-function genPropsFromTS(ctx: ScriptCompileContext) {
- const keys = Object.keys(ctx.typeDeclaredProps)
- if (!keys.length) return
-
+function genRuntimePropsFromTypes(ctx: ScriptCompileContext) {
+ const propStrings: string[] = []
const hasStaticDefaults = hasStaticWithDefaults(ctx)
- let propsDecls = `{
- ${keys
- .map(key => {
- let defaultString: string | undefined
- const destructured = genDestructuredDefaultValue(
- ctx,
- key,
- ctx.typeDeclaredProps[key].type
+
+ // this is only called if propsTypeDecl exists
+ const node = ctx.propsTypeDecl!
+ const members = node.type === 'TSTypeLiteral' ? node.members : node.body
+ for (const m of members) {
+ if (
+ (m.type === 'TSPropertySignature' || m.type === 'TSMethodSignature') &&
+ m.key.type === 'Identifier'
+ ) {
+ const key = m.key.name
+ let type: string[] | undefined
+ let skipCheck = false
+ if (m.type === 'TSMethodSignature') {
+ type = ['Function']
+ } else if (m.typeAnnotation) {
+ type = inferRuntimeType(
+ m.typeAnnotation.typeAnnotation,
+ ctx.declaredTypes
)
- if (destructured) {
- defaultString = `default: ${destructured.valueString}${
- destructured.needSkipFactory ? `, skipFactory: true` : ``
- }`
- } else if (hasStaticDefaults) {
- const prop = (
- ctx.propsRuntimeDefaults as ObjectExpression
- ).properties.find(node => {
- if (node.type === 'SpreadElement') return false
- return resolveObjectKey(node.key, node.computed) === key
- }) as ObjectProperty | ObjectMethod
- if (prop) {
- if (prop.type === 'ObjectProperty') {
- // prop has corresponding static default value
- defaultString = `default: ${ctx.getString(prop.value)}`
- } else {
- defaultString = `${prop.async ? 'async ' : ''}${
- prop.kind !== 'method' ? `${prop.kind} ` : ''
- }default() ${ctx.getString(prop.body)}`
- }
+ // skip check for result containing unknown types
+ if (type.includes(UNKNOWN_TYPE)) {
+ if (type.includes('Boolean') || type.includes('Function')) {
+ type = type.filter(t => t !== UNKNOWN_TYPE)
+ skipCheck = true
+ } else {
+ type = ['null']
}
}
+ }
- const { type, required, skipCheck } = ctx.typeDeclaredProps[key]
- if (!ctx.options.isProd) {
- return `${key}: { ${concatStrings([
- `type: ${toRuntimeTypeString(type)}`,
- `required: ${required}`,
- skipCheck && 'skipCheck: true',
- defaultString
- ])} }`
- } else if (
- type.some(
- el =>
- el === 'Boolean' ||
- ((!hasStaticDefaults || defaultString) && el === 'Function')
- )
- ) {
- // #4783 for boolean, should keep the type
- // #7111 for function, if default value exists or it's not static, should keep it
- // in production
- return `${key}: { ${concatStrings([
- `type: ${toRuntimeTypeString(type)}`,
- defaultString
- ])} }`
- } else {
- // production: checks are useless
- return `${key}: ${defaultString ? `{ ${defaultString} }` : `{}`}`
- }
- })
- .join(',\n ')}\n }`
+ propStrings.push(
+ genRuntimePropFromType(
+ ctx,
+ key,
+ !m.optional,
+ type || [`null`],
+ skipCheck,
+ hasStaticDefaults
+ )
+ )
+
+ // register bindings
+ ctx.bindingMetadata[key] = BindingTypes.PROPS
+ }
+ }
+
+ if (!propStrings.length) {
+ return
+ }
+
+ let propsDecls = `{
+ ${propStrings.join(',\n ')}\n }`
if (ctx.propsRuntimeDefaults && !hasStaticDefaults) {
propsDecls = `${ctx.helper('mergeDefaults')}(${propsDecls}, ${ctx.getString(
return propsDecls
}
+function genRuntimePropFromType(
+ ctx: ScriptCompileContext,
+ key: string,
+ required: boolean,
+ type: string[],
+ skipCheck: boolean,
+ hasStaticDefaults: boolean
+): string {
+ let defaultString: string | undefined
+ const destructured = genDestructuredDefaultValue(ctx, key, type)
+ if (destructured) {
+ defaultString = `default: ${destructured.valueString}${
+ destructured.needSkipFactory ? `, skipFactory: true` : ``
+ }`
+ } else if (hasStaticDefaults) {
+ const prop = (ctx.propsRuntimeDefaults as ObjectExpression).properties.find(
+ node => {
+ if (node.type === 'SpreadElement') return false
+ return resolveObjectKey(node.key, node.computed) === key
+ }
+ ) as ObjectProperty | ObjectMethod
+ if (prop) {
+ if (prop.type === 'ObjectProperty') {
+ // prop has corresponding static default value
+ defaultString = `default: ${ctx.getString(prop.value)}`
+ } else {
+ defaultString = `${prop.async ? 'async ' : ''}${
+ prop.kind !== 'method' ? `${prop.kind} ` : ''
+ }default() ${ctx.getString(prop.body)}`
+ }
+ }
+ }
+
+ if (!ctx.options.isProd) {
+ return `${key}: { ${concatStrings([
+ `type: ${toRuntimeTypeString(type)}`,
+ `required: ${required}`,
+ skipCheck && 'skipCheck: true',
+ defaultString
+ ])} }`
+ } else if (
+ type.some(
+ el =>
+ el === 'Boolean' ||
+ ((!hasStaticDefaults || defaultString) && el === 'Function')
+ )
+ ) {
+ // #4783 for boolean, should keep the type
+ // #7111 for function, if default value exists or it's not static, should keep it
+ // in production
+ return `${key}: { ${concatStrings([
+ `type: ${toRuntimeTypeString(type)}`,
+ defaultString
+ ])} }`
+ } else {
+ // production: checks are useless
+ return `${key}: ${defaultString ? `{ ${defaultString} }` : `{}`}`
+ }
+}
+
/**
* check defaults. If the default object is an object literal with only
* static properties, we can directly generate more optimized default
* declarations. Otherwise we will have to fallback to runtime merging.
*/
function hasStaticWithDefaults(ctx: ScriptCompileContext) {
- return (
+ return !!(
ctx.propsRuntimeDefaults &&
ctx.propsRuntimeDefaults.type === 'ObjectExpression' &&
ctx.propsRuntimeDefaults.properties.every(