mirror of https://github.com/vuejs/core.git
refactor(compiler-sfc): extract processDefineModel, move main magic string instance to context
This commit is contained in:
parent
9f5692e052
commit
9f2ca5155e
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue