refactor(compiler-sfc): extract processDefineModel, move main magic string instance to context

This commit is contained in:
Evan You 2023-04-11 14:17:12 +08:00
parent 9f5692e052
commit 9f2ca5155e
3 changed files with 143 additions and 135 deletions

View File

@ -26,7 +26,6 @@ import {
TSType, TSType,
TSTypeLiteral, TSTypeLiteral,
TSFunctionType, TSFunctionType,
ObjectProperty,
ArrayExpression, ArrayExpression,
Statement, Statement,
CallExpression, CallExpression,
@ -54,9 +53,11 @@ import { transformDestructuredProps } from './script/definePropsDestructure'
import { ScriptCompileContext } from './script/context' import { ScriptCompileContext } from './script/context'
import { import {
processDefineProps, processDefineProps,
genRuntimeProps,
DEFINE_PROPS, DEFINE_PROPS,
WITH_DEFAULTS WITH_DEFAULTS
} from './script/defineProps' } from './script/defineProps'
import { DEFINE_MODEL, processDefineModel } from './script/defineModel'
import { import {
resolveObjectKey, resolveObjectKey,
FromNormalScript, FromNormalScript,
@ -65,14 +66,12 @@ import {
unwrapTSNode, unwrapTSNode,
isCallOf isCallOf
} from './script/utils' } from './script/utils'
import { genRuntimeProps } from './script/defineProps'
// Special compiler macros // Special compiler macros
const DEFINE_EMITS = 'defineEmits' const DEFINE_EMITS = 'defineEmits'
const DEFINE_EXPOSE = 'defineExpose' const DEFINE_EXPOSE = 'defineExpose'
const DEFINE_OPTIONS = 'defineOptions' const DEFINE_OPTIONS = 'defineOptions'
const DEFINE_SLOTS = 'defineSlots' const DEFINE_SLOTS = 'defineSlots'
const DEFINE_MODEL = 'defineModel'
const isBuiltInDir = makeMap( const isBuiltInDir = makeMap(
`once,memo,if,for,else,else-if,slot,text,html,on,bind,model,show,cloak,is` `once,memo,if,for,else,else-if,slot,text,html,on,bind,model,show,cloak,is`
@ -170,7 +169,6 @@ export function compileScript(
// feature flags // feature flags
// TODO remove in 3.4 // TODO remove in 3.4
const enableReactivityTransform = !!options.reactivityTransform const enableReactivityTransform = !!options.reactivityTransform
const enableDefineModel = !!options.defineModel
const isProd = !!options.isProd const isProd = !!options.isProd
const genSourceMap = options.sourceMap !== false const genSourceMap = options.sourceMap !== false
const hoistStatic = options.hoistStatic !== false && !script const hoistStatic = options.hoistStatic !== false && !script
@ -298,7 +296,7 @@ export function compileScript(
// const declaredTypes: Record<string, string[]> = {} // const declaredTypes: Record<string, string[]> = {}
// magic-string state // magic-string state
const s = new MagicString(source) // const s = new MagicString(source)
const startOffset = scriptSetup.loc.start.offset const startOffset = scriptSetup.loc.start.offset
const endOffset = scriptSetup.loc.end.offset const endOffset = scriptSetup.loc.end.offset
const scriptStartOffset = script && script.loc.start.offset const scriptStartOffset = script && script.loc.start.offset
@ -334,7 +332,7 @@ export function compileScript(
} }
end++ end++
} }
s.move(start, end, 0) ctx.s.move(start, end, 0)
} }
function registerUserImport( function registerUserImport(
@ -425,7 +423,7 @@ export function compileScript(
} }
if (declId) { if (declId) {
s.overwrite( ctx.s.overwrite(
startOffset + node.start!, startOffset + node.start!,
startOffset + node.end!, startOffset + node.end!,
`${ctx.helper('useSlots')}()` `${ctx.helper('useSlots')}()`
@ -435,79 +433,6 @@ export function compileScript(
return true return true
} }
function processDefineModel(node: Node, declId?: LVal): boolean {
if (!enableDefineModel || !isCallOf(node, DEFINE_MODEL)) {
return false
}
ctx.hasDefineModelCall = true
const type =
(node.typeParameters && node.typeParameters.params[0]) || undefined
let modelName: string
let options: Node | undefined
const arg0 = node.arguments[0] && unwrapTSNode(node.arguments[0])
if (arg0 && arg0.type === 'StringLiteral') {
modelName = arg0.value
options = node.arguments[1]
} else {
modelName = 'modelValue'
options = arg0
}
if (ctx.modelDecls[modelName]) {
error(`duplicate model name ${JSON.stringify(modelName)}`, node)
}
const optionsString = options
? s.slice(startOffset + options.start!, startOffset + options.end!)
: undefined
ctx.modelDecls[modelName] = {
type,
options: optionsString,
identifier:
declId && declId.type === 'Identifier' ? declId.name : undefined
}
let runtimeOptions = ''
if (options) {
if (options.type === 'ObjectExpression') {
const local = options.properties.find(
p =>
p.type === 'ObjectProperty' &&
((p.key.type === 'Identifier' && p.key.name === 'local') ||
(p.key.type === 'StringLiteral' && p.key.value === 'local'))
) as ObjectProperty
if (local) {
runtimeOptions = `{ ${s.slice(
startOffset + local.start!,
startOffset + local.end!
)} }`
} else {
for (const p of options.properties) {
if (p.type === 'SpreadElement' || p.computed) {
runtimeOptions = optionsString!
break
}
}
}
} else {
runtimeOptions = optionsString!
}
}
s.overwrite(
startOffset + node.start!,
startOffset + node.end!,
`${ctx.helper('useModel')}(__props, ${JSON.stringify(modelName)}${
runtimeOptions ? `, ${runtimeOptions}` : ``
})`
)
return true
}
function getAstBody(): Statement[] { function getAstBody(): Statement[] {
return scriptAst return scriptAst
? [...scriptSetupAst.body, ...scriptAst.body] ? [...scriptSetupAst.body, ...scriptAst.body]
@ -743,14 +668,14 @@ export function compileScript(
const containsNestedAwait = /\bawait\b/.test(argumentStr) const containsNestedAwait = /\bawait\b/.test(argumentStr)
s.overwrite( ctx.s.overwrite(
node.start! + startOffset, node.start! + startOffset,
argumentStart + startOffset, argumentStart + startOffset,
`${needSemi ? `;` : ``}(\n ([__temp,__restore] = ${ctx.helper( `${needSemi ? `;` : ``}(\n ([__temp,__restore] = ${ctx.helper(
`withAsyncContext` `withAsyncContext`
)}(${containsNestedAwait ? `async ` : ``}() => ` )}(${containsNestedAwait ? `async ` : ``}() => `
) )
s.appendLeft( ctx.s.appendLeft(
node.end! + startOffset, node.end! + startOffset,
`)),\n ${isStatement ? `` : `__temp = `}await __temp,\n __restore()${ `)),\n ${isStatement ? `` : `__temp = `}await __temp,\n __restore()${
isStatement ? `` : `,\n __temp` isStatement ? `` : `,\n __temp`
@ -824,7 +749,7 @@ export function compileScript(
removed++ removed++
const current = node.specifiers[i] const current = node.specifiers[i]
const next = node.specifiers[i + 1] const next = node.specifiers[i + 1]
s.remove( ctx.s.remove(
removeLeft removeLeft
? node.specifiers[i - 1].end! + startOffset ? node.specifiers[i - 1].end! + startOffset
: current.start! + startOffset, : current.start! + startOffset,
@ -871,7 +796,7 @@ export function compileScript(
} }
} }
if (node.specifiers.length && removed === node.specifiers.length) { if (node.specifiers.length && removed === node.specifiers.length) {
s.remove(node.start! + startOffset, node.end! + startOffset) ctx.s.remove(node.start! + startOffset, node.end! + startOffset)
} }
} }
} }
@ -906,18 +831,18 @@ export function compileScript(
optionProperties = defaultExport.declaration.arguments[0].properties optionProperties = defaultExport.declaration.arguments[0].properties
} }
if (optionProperties) { if (optionProperties) {
for (const s of optionProperties) { for (const p of optionProperties) {
if ( if (
s.type === 'ObjectProperty' && p.type === 'ObjectProperty' &&
s.key.type === 'Identifier' && p.key.type === 'Identifier' &&
s.key.name === 'name' p.key.name === 'name'
) { ) {
ctx.hasDefaultExportName = true ctx.hasDefaultExportName = true
} }
if ( if (
(s.type === 'ObjectMethod' || s.type === 'ObjectProperty') && (p.type === 'ObjectMethod' || p.type === 'ObjectProperty') &&
s.key.type === 'Identifier' && p.key.type === 'Identifier' &&
s.key.name === 'render' p.key.name === 'render'
) { ) {
// TODO warn when we provide a better way to do it? // TODO warn when we provide a better way to do it?
ctx.hasDefaultExportRender = true ctx.hasDefaultExportRender = true
@ -928,7 +853,7 @@ export function compileScript(
// export default { ... } --> const __default__ = { ... } // export default { ... } --> const __default__ = { ... }
const start = node.start! + scriptStartOffset! const start = node.start! + scriptStartOffset!
const end = node.declaration.start! + scriptStartOffset! const end = node.declaration.start! + scriptStartOffset!
s.overwrite(start, end, `const ${normalScriptDefaultVar} = `) ctx.s.overwrite(start, end, `const ${normalScriptDefaultVar} = `)
} else if (node.type === 'ExportNamedDeclaration') { } else if (node.type === 'ExportNamedDeclaration') {
const defaultSpecifier = node.specifiers.find( const defaultSpecifier = node.specifiers.find(
s => s.exported.type === 'Identifier' && s.exported.name === 'default' s => s.exported.type === 'Identifier' && s.exported.name === 'default'
@ -937,12 +862,12 @@ export function compileScript(
defaultExport = node defaultExport = node
// 1. remove specifier // 1. remove specifier
if (node.specifiers.length > 1) { if (node.specifiers.length > 1) {
s.remove( ctx.s.remove(
defaultSpecifier.start! + scriptStartOffset!, defaultSpecifier.start! + scriptStartOffset!,
defaultSpecifier.end! + scriptStartOffset! defaultSpecifier.end! + scriptStartOffset!
) )
} else { } else {
s.remove( ctx.s.remove(
node.start! + scriptStartOffset!, node.start! + scriptStartOffset!,
node.end! + scriptStartOffset! node.end! + scriptStartOffset!
) )
@ -951,13 +876,13 @@ export function compileScript(
// export { x as default } from './x' // export { x as default } from './x'
// rewrite to `import { x as __default__ } from './x'` and // rewrite to `import { x as __default__ } from './x'` and
// add to top // add to top
s.prepend( ctx.s.prepend(
`import { ${defaultSpecifier.local.name} as ${normalScriptDefaultVar} } from '${node.source.value}'\n` `import { ${defaultSpecifier.local.name} as ${normalScriptDefaultVar} } from '${node.source.value}'\n`
) )
} else { } else {
// export { x as default } // export { x as default }
// rewrite to `const __default__ = x` and move to end // rewrite to `const __default__ = x` and move to end
s.appendLeft( ctx.s.appendLeft(
scriptEndOffset!, scriptEndOffset!,
`\nconst ${normalScriptDefaultVar} = ${defaultSpecifier.local.name}\n` `\nconst ${normalScriptDefaultVar} = ${defaultSpecifier.local.name}\n`
) )
@ -994,7 +919,7 @@ export function compileScript(
if (enableReactivityTransform && shouldTransform(script.content)) { if (enableReactivityTransform && shouldTransform(script.content)) {
const { rootRefs, importedHelpers } = transformAST( const { rootRefs, importedHelpers } = transformAST(
scriptAst, scriptAst,
s, ctx.s,
scriptStartOffset! scriptStartOffset!
) )
refBindings = rootRefs refBindings = rootRefs
@ -1009,9 +934,9 @@ export function compileScript(
if (scriptStartOffset! > startOffset) { if (scriptStartOffset! > startOffset) {
// if content doesn't end with newline, add one // if content doesn't end with newline, add one
if (!/\n$/.test(script.content.trim())) { if (!/\n$/.test(script.content.trim())) {
s.appendLeft(scriptEndOffset!, `\n`) ctx.s.appendLeft(scriptEndOffset!, `\n`)
} }
s.move(scriptStartOffset!, scriptEndOffset!, 0) ctx.s.move(scriptStartOffset!, scriptEndOffset!, 0)
} }
} }
@ -1041,17 +966,17 @@ export function compileScript(
processDefineOptions(expr) || processDefineOptions(expr) ||
processDefineSlots(expr) processDefineSlots(expr)
) { ) {
s.remove(node.start! + startOffset, node.end! + startOffset) ctx.s.remove(node.start! + startOffset, node.end! + startOffset)
} else if (processDefineExpose(expr)) { } else if (processDefineExpose(expr)) {
// defineExpose({}) -> expose({}) // defineExpose({}) -> expose({})
const callee = (expr as CallExpression).callee const callee = (expr as CallExpression).callee
s.overwrite( ctx.s.overwrite(
callee.start! + startOffset, callee.start! + startOffset,
callee.end! + startOffset, callee.end! + startOffset,
'__expose' '__expose'
) )
} else { } else {
processDefineModel(expr) processDefineModel(ctx, expr)
} }
} }
@ -1077,11 +1002,11 @@ export function compileScript(
!isDefineProps && processDefineEmits(init, decl.id) !isDefineProps && processDefineEmits(init, decl.id)
!isDefineEmits && !isDefineEmits &&
(processDefineSlots(init, decl.id) || (processDefineSlots(init, decl.id) ||
processDefineModel(init, decl.id)) processDefineModel(ctx, init, decl.id))
if (isDefineProps || isDefineEmits) { if (isDefineProps || isDefineEmits) {
if (left === 1) { if (left === 1) {
s.remove(node.start! + startOffset, node.end! + startOffset) ctx.s.remove(node.start! + startOffset, node.end! + startOffset)
} else { } else {
let start = decl.start! + startOffset let start = decl.start! + startOffset
let end = decl.end! + startOffset let end = decl.end! + startOffset
@ -1094,7 +1019,7 @@ export function compileScript(
// not the last one, locate the start of the next // not the last one, locate the start of the next
end = node.declarations[i + 1].start! + startOffset end = node.declarations[i + 1].start! + startOffset
} }
s.remove(start, end) ctx.s.remove(start, end)
left-- left--
} }
} else { } else {
@ -1202,7 +1127,7 @@ export function compileScript(
if (ctx.propsDestructureDecl) { if (ctx.propsDestructureDecl) {
transformDestructuredProps( transformDestructuredProps(
scriptSetupAst, scriptSetupAst,
s, ctx.s,
startOffset, startOffset,
ctx.propsDestructuredBindings, ctx.propsDestructuredBindings,
error, error,
@ -1219,7 +1144,7 @@ export function compileScript(
) { ) {
const { rootRefs, importedHelpers } = transformAST( const { rootRefs, importedHelpers } = transformAST(
scriptSetupAst, scriptSetupAst,
s, ctx.s,
startOffset, startOffset,
refBindings refBindings
) )
@ -1246,19 +1171,19 @@ export function compileScript(
if (script) { if (script) {
if (startOffset < scriptStartOffset!) { if (startOffset < scriptStartOffset!) {
// <script setup> before <script> // <script setup> before <script>
s.remove(0, startOffset) ctx.s.remove(0, startOffset)
s.remove(endOffset, scriptStartOffset!) ctx.s.remove(endOffset, scriptStartOffset!)
s.remove(scriptEndOffset!, source.length) ctx.s.remove(scriptEndOffset!, source.length)
} else { } else {
// <script> before <script setup> // <script> before <script setup>
s.remove(0, scriptStartOffset!) ctx.s.remove(0, scriptStartOffset!)
s.remove(scriptEndOffset!, startOffset) ctx.s.remove(scriptEndOffset!, startOffset)
s.remove(endOffset, source.length) ctx.s.remove(endOffset, source.length)
} }
} else { } else {
// only <script setup> // only <script setup>
s.remove(0, startOffset) ctx.s.remove(0, startOffset)
s.remove(endOffset, source.length) ctx.s.remove(endOffset, source.length)
} }
// 7. analyze binding metadata // 7. analyze binding metadata
@ -1320,7 +1245,7 @@ export function compileScript(
) { ) {
ctx.helperImports.add(CSS_VARS_HELPER) ctx.helperImports.add(CSS_VARS_HELPER)
ctx.helperImports.add('unref') ctx.helperImports.add('unref')
s.prependLeft( ctx.s.prependLeft(
startOffset, startOffset,
`\n${genCssVarsCode(cssVars, ctx.bindingMetadata, scopeId, isProd)}\n` `\n${genCssVarsCode(cssVars, ctx.bindingMetadata, scopeId, isProd)}\n`
) )
@ -1338,10 +1263,13 @@ export function compileScript(
// we use a default __props so that template expressions referencing props // we use a default __props so that template expressions referencing props
// can use it directly // can use it directly
if (ctx.propsIdentifier) { if (ctx.propsIdentifier) {
s.prependLeft(startOffset, `\nconst ${ctx.propsIdentifier} = __props;\n`) ctx.s.prependLeft(
startOffset,
`\nconst ${ctx.propsIdentifier} = __props;\n`
)
} }
if (ctx.propsDestructureRestId) { if (ctx.propsDestructureRestId) {
s.prependLeft( ctx.s.prependLeft(
startOffset, startOffset,
`\nconst ${ctx.propsDestructureRestId} = ${ctx.helper( `\nconst ${ctx.propsDestructureRestId} = ${ctx.helper(
`createPropsRestProxy` `createPropsRestProxy`
@ -1353,7 +1281,7 @@ export function compileScript(
// inject temp variables for async context preservation // inject temp variables for async context preservation
if (hasAwait) { if (hasAwait) {
const any = isTS ? `: any` : `` const any = isTS ? `: any` : ``
s.prependLeft(startOffset, `\nlet __temp${any}, __restore${any}\n`) ctx.s.prependLeft(startOffset, `\nlet __temp${any}, __restore${any}\n`)
} }
const destructureElements = const destructureElements =
@ -1454,7 +1382,7 @@ export function compileScript(
throw err throw err
} }
if (preamble) { if (preamble) {
s.prepend(preamble) ctx.s.prepend(preamble)
} }
// avoid duplicated unref import // avoid duplicated unref import
// as this may get injected by the render function preamble OR the // as this may get injected by the render function preamble OR the
@ -1471,7 +1399,7 @@ export function compileScript(
if (!options.inlineTemplate && !__TEST__) { if (!options.inlineTemplate && !__TEST__) {
// in non-inline mode, the `__isScriptSetup: true` flag is used by // in non-inline mode, the `__isScriptSetup: true` flag is used by
// componentPublicInstance proxy to allow properties that start with $ or _ // componentPublicInstance proxy to allow properties that start with $ or _
s.appendRight( ctx.s.appendRight(
endOffset, endOffset,
`\nconst __returned__ = ${returned}\n` + `\nconst __returned__ = ${returned}\n` +
`Object.defineProperty(__returned__, '__isScriptSetup', { enumerable: false, value: true })\n` + `Object.defineProperty(__returned__, '__isScriptSetup', { enumerable: false, value: true })\n` +
@ -1479,7 +1407,7 @@ export function compileScript(
`\n}\n\n` `\n}\n\n`
) )
} else { } else {
s.appendRight(endOffset, `\nreturn ${returned}\n}\n\n`) ctx.s.appendRight(endOffset, `\nreturn ${returned}\n}\n\n`)
} }
// 11. finalize default export // 11. finalize default export
@ -1521,7 +1449,7 @@ export function compileScript(
const def = const def =
(defaultExport ? `\n ...${normalScriptDefaultVar},` : ``) + (defaultExport ? `\n ...${normalScriptDefaultVar},` : ``) +
(definedOptions ? `\n ...${definedOptions},` : '') (definedOptions ? `\n ...${definedOptions},` : '')
s.prependLeft( ctx.s.prependLeft(
startOffset, startOffset,
`\n${genDefaultAs} /*#__PURE__*/${ctx.helper( `\n${genDefaultAs} /*#__PURE__*/${ctx.helper(
`defineComponent` `defineComponent`
@ -1529,47 +1457,47 @@ export function compileScript(
hasAwait ? `async ` : `` hasAwait ? `async ` : ``
}setup(${args}) {\n${exposeCall}` }setup(${args}) {\n${exposeCall}`
) )
s.appendRight(endOffset, `})`) ctx.s.appendRight(endOffset, `})`)
} else { } else {
if (defaultExport || definedOptions) { if (defaultExport || definedOptions) {
// without TS, can't rely on rest spread, so we use Object.assign // without TS, can't rely on rest spread, so we use Object.assign
// export default Object.assign(__default__, { ... }) // export default Object.assign(__default__, { ... })
s.prependLeft( ctx.s.prependLeft(
startOffset, startOffset,
`\n${genDefaultAs} /*#__PURE__*/Object.assign(${ `\n${genDefaultAs} /*#__PURE__*/Object.assign(${
defaultExport ? `${normalScriptDefaultVar}, ` : '' defaultExport ? `${normalScriptDefaultVar}, ` : ''
}${definedOptions ? `${definedOptions}, ` : ''}{${runtimeOptions}\n ` + }${definedOptions ? `${definedOptions}, ` : ''}{${runtimeOptions}\n ` +
`${hasAwait ? `async ` : ``}setup(${args}) {\n${exposeCall}` `${hasAwait ? `async ` : ``}setup(${args}) {\n${exposeCall}`
) )
s.appendRight(endOffset, `})`) ctx.s.appendRight(endOffset, `})`)
} else { } else {
s.prependLeft( ctx.s.prependLeft(
startOffset, startOffset,
`\n${genDefaultAs} {${runtimeOptions}\n ` + `\n${genDefaultAs} {${runtimeOptions}\n ` +
`${hasAwait ? `async ` : ``}setup(${args}) {\n${exposeCall}` `${hasAwait ? `async ` : ``}setup(${args}) {\n${exposeCall}`
) )
s.appendRight(endOffset, `}`) ctx.s.appendRight(endOffset, `}`)
} }
} }
// 12. finalize Vue helper imports // 12. finalize Vue helper imports
if (ctx.helperImports.size > 0) { if (ctx.helperImports.size > 0) {
s.prepend( ctx.s.prepend(
`import { ${[...ctx.helperImports] `import { ${[...ctx.helperImports]
.map(h => `${h} as _${h}`) .map(h => `${h} as _${h}`)
.join(', ')} } from 'vue'\n` .join(', ')} } from 'vue'\n`
) )
} }
s.trim() ctx.s.trim()
return { return {
...scriptSetup, ...scriptSetup,
bindings: ctx.bindingMetadata, bindings: ctx.bindingMetadata,
imports: userImports, imports: userImports,
content: s.toString(), content: ctx.s.toString(),
map: genSourceMap map: genSourceMap
? (s.generateMap({ ? (ctx.s.generateMap({
source: filename, source: filename,
hires: true, hires: true,
includeContent: true includeContent: true

View File

@ -6,6 +6,7 @@ import { SFCScriptCompileOptions } from '../compileScript'
import { PropsDeclType, PropsDestructureBindings } from './defineProps' import { PropsDeclType, PropsDestructureBindings } from './defineProps'
import { ModelDecl } from './defineModel' import { ModelDecl } from './defineModel'
import { BindingMetadata } from '../../../compiler-core/src' import { BindingMetadata } from '../../../compiler-core/src'
import MagicString from 'magic-string'
export class ScriptCompileContext { export class ScriptCompileContext {
isJS: boolean isJS: boolean
@ -14,7 +15,7 @@ export class ScriptCompileContext {
scriptAst: Program | null scriptAst: Program | null
scriptSetupAst: Program | null scriptSetupAst: Program | null
// s = new MagicString(this.descriptor.source) s = new MagicString(this.descriptor.source)
startOffset = this.descriptor.scriptSetup?.loc.start.offset startOffset = this.descriptor.scriptSetup?.loc.start.offset
endOffset = this.descriptor.scriptSetup?.loc.end.offset endOffset = this.descriptor.scriptSetup?.loc.end.offset
scriptStartOffset = this.descriptor.script?.loc.start.offset scriptStartOffset = this.descriptor.script?.loc.start.offset

View File

@ -1,7 +1,15 @@
import { TSType } from '@babel/types' import { LVal, Node, ObjectProperty, TSType } from '@babel/types'
import { ScriptCompileContext } from './context' import { ScriptCompileContext } from './context'
import { inferRuntimeType } from './resolveType' import { inferRuntimeType } from './resolveType'
import { UNKNOWN_TYPE, concatStrings, toRuntimeTypeString } from './utils' import {
UNKNOWN_TYPE,
concatStrings,
isCallOf,
toRuntimeTypeString,
unwrapTSNode
} from './utils'
export const DEFINE_MODEL = 'defineModel'
export interface ModelDecl { export interface ModelDecl {
type: TSType | undefined type: TSType | undefined
@ -9,6 +17,77 @@ export interface ModelDecl {
identifier: string | undefined identifier: string | undefined
} }
export function processDefineModel(
ctx: ScriptCompileContext,
node: Node,
declId?: LVal
): boolean {
if (!ctx.options.defineModel || !isCallOf(node, DEFINE_MODEL)) {
return false
}
ctx.hasDefineModelCall = true
const type =
(node.typeParameters && node.typeParameters.params[0]) || undefined
let modelName: string
let options: Node | undefined
const arg0 = node.arguments[0] && unwrapTSNode(node.arguments[0])
if (arg0 && arg0.type === 'StringLiteral') {
modelName = arg0.value
options = node.arguments[1]
} else {
modelName = 'modelValue'
options = arg0
}
if (ctx.modelDecls[modelName]) {
ctx.error(`duplicate model name ${JSON.stringify(modelName)}`, node)
}
const optionsString = options && ctx.getString(options)
ctx.modelDecls[modelName] = {
type,
options: optionsString,
identifier: declId && declId.type === 'Identifier' ? declId.name : undefined
}
let runtimeOptions = ''
if (options) {
if (options.type === 'ObjectExpression') {
const local = options.properties.find(
p =>
p.type === 'ObjectProperty' &&
((p.key.type === 'Identifier' && p.key.name === 'local') ||
(p.key.type === 'StringLiteral' && p.key.value === 'local'))
) as ObjectProperty
if (local) {
runtimeOptions = `{ ${ctx.getString(local)} }`
} else {
for (const p of options.properties) {
if (p.type === 'SpreadElement' || p.computed) {
runtimeOptions = optionsString!
break
}
}
}
} else {
runtimeOptions = optionsString!
}
}
ctx.s.overwrite(
ctx.startOffset! + node.start!,
ctx.startOffset! + node.end!,
`${ctx.helper('useModel')}(__props, ${JSON.stringify(modelName)}${
runtimeOptions ? `, ${runtimeOptions}` : ``
})`
)
return true
}
export function genModelProps(ctx: ScriptCompileContext) { export function genModelProps(ctx: ScriptCompileContext) {
if (!ctx.hasDefineModelCall) return if (!ctx.hasDefineModelCall) return