mirror of https://github.com/vuejs/core.git
refactor(compiler-sfc): split all macros
This commit is contained in:
parent
c52157c87d
commit
3da1bb36b1
|
@ -18,8 +18,6 @@ import {
|
||||||
ExportSpecifier,
|
ExportSpecifier,
|
||||||
Statement,
|
Statement,
|
||||||
CallExpression,
|
CallExpression,
|
||||||
AwaitExpression,
|
|
||||||
LVal,
|
|
||||||
TSEnumDeclaration
|
TSEnumDeclaration
|
||||||
} from '@babel/types'
|
} from '@babel/types'
|
||||||
import { walk } from 'estree-walker'
|
import { walk } from 'estree-walker'
|
||||||
|
@ -46,16 +44,15 @@ import {
|
||||||
genRuntimeEmits,
|
genRuntimeEmits,
|
||||||
DEFINE_EMITS
|
DEFINE_EMITS
|
||||||
} from './script/defineEmits'
|
} from './script/defineEmits'
|
||||||
|
import { DEFINE_EXPOSE, processDefineExpose } from './script/defineExpose'
|
||||||
|
import { DEFINE_OPTIONS, processDefineOptions } from './script/defineOptions'
|
||||||
|
import { processDefineSlots } from './script/defineSlots'
|
||||||
import { DEFINE_MODEL, processDefineModel } from './script/defineModel'
|
import { DEFINE_MODEL, processDefineModel } from './script/defineModel'
|
||||||
import { isLiteralNode, unwrapTSNode, isCallOf } from './script/utils'
|
import { isLiteralNode, unwrapTSNode, isCallOf } from './script/utils'
|
||||||
import { inferRuntimeType } from './script/resolveType'
|
import { inferRuntimeType } from './script/resolveType'
|
||||||
import { analyzeScriptBindings } from './script/analyzeScriptBindings'
|
import { analyzeScriptBindings } from './script/analyzeScriptBindings'
|
||||||
import { isImportUsed } from './script/importUsageCheck'
|
import { isImportUsed } from './script/importUsageCheck'
|
||||||
|
import { processAwait } from './script/topLevelAwait'
|
||||||
// Special compiler macros
|
|
||||||
const DEFINE_EXPOSE = 'defineExpose'
|
|
||||||
const DEFINE_OPTIONS = 'defineOptions'
|
|
||||||
const DEFINE_SLOTS = 'defineSlots'
|
|
||||||
|
|
||||||
export interface SFCScriptCompileOptions {
|
export interface SFCScriptCompileOptions {
|
||||||
/**
|
/**
|
||||||
|
@ -250,30 +247,15 @@ export function compileScript(
|
||||||
const setupBindings: Record<string, BindingTypes> = Object.create(null)
|
const setupBindings: Record<string, BindingTypes> = Object.create(null)
|
||||||
|
|
||||||
let defaultExport: Node | undefined
|
let defaultExport: Node | undefined
|
||||||
let optionsRuntimeDecl: Node | undefined
|
|
||||||
let hasAwait = false
|
let hasAwait = false
|
||||||
let hasInlinedSsrRenderFn = false
|
let hasInlinedSsrRenderFn = false
|
||||||
|
|
||||||
// magic-string state
|
// string offsets
|
||||||
const startOffset = scriptSetup.loc.start.offset
|
const startOffset = ctx.startOffset!
|
||||||
const endOffset = scriptSetup.loc.end.offset
|
const endOffset = ctx.endOffset!
|
||||||
const scriptStartOffset = script && script.loc.start.offset
|
const scriptStartOffset = script && script.loc.start.offset
|
||||||
const scriptEndOffset = script && script.loc.end.offset
|
const scriptEndOffset = script && script.loc.end.offset
|
||||||
|
|
||||||
function error(
|
|
||||||
msg: string,
|
|
||||||
node: Node,
|
|
||||||
end: number = node.end! + startOffset
|
|
||||||
): never {
|
|
||||||
throw new Error(
|
|
||||||
`[@vue/compiler-sfc] ${msg}\n\n${sfc.filename}\n${generateCodeFrame(
|
|
||||||
source,
|
|
||||||
node.start! + startOffset,
|
|
||||||
end
|
|
||||||
)}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function hoistNode(node: Statement) {
|
function hoistNode(node: Statement) {
|
||||||
const start = node.start! + startOffset
|
const start = node.start! + startOffset
|
||||||
let end = node.end! + startOffset
|
let end = node.end! + startOffset
|
||||||
|
@ -324,108 +306,12 @@ export function compileScript(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processDefineSlots(node: Node, declId?: LVal): boolean {
|
|
||||||
if (!isCallOf(node, DEFINE_SLOTS)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (ctx.hasDefineSlotsCall) {
|
|
||||||
error(`duplicate ${DEFINE_SLOTS}() call`, node)
|
|
||||||
}
|
|
||||||
ctx.hasDefineSlotsCall = true
|
|
||||||
|
|
||||||
if (node.arguments.length > 0) {
|
|
||||||
error(`${DEFINE_SLOTS}() cannot accept arguments`, node)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (declId) {
|
|
||||||
ctx.s.overwrite(
|
|
||||||
startOffset + node.start!,
|
|
||||||
startOffset + node.end!,
|
|
||||||
`${ctx.helper('useSlots')}()`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
function processDefineOptions(node: Node): boolean {
|
|
||||||
if (!isCallOf(node, DEFINE_OPTIONS)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (ctx.hasDefineOptionsCall) {
|
|
||||||
error(`duplicate ${DEFINE_OPTIONS}() call`, node)
|
|
||||||
}
|
|
||||||
if (node.typeParameters) {
|
|
||||||
error(`${DEFINE_OPTIONS}() cannot accept type arguments`, node)
|
|
||||||
}
|
|
||||||
if (!node.arguments[0]) return true
|
|
||||||
|
|
||||||
ctx.hasDefineOptionsCall = true
|
|
||||||
optionsRuntimeDecl = unwrapTSNode(node.arguments[0])
|
|
||||||
|
|
||||||
let propsOption = undefined
|
|
||||||
let emitsOption = undefined
|
|
||||||
let exposeOption = undefined
|
|
||||||
let slotsOption = undefined
|
|
||||||
if (optionsRuntimeDecl.type === 'ObjectExpression') {
|
|
||||||
for (const prop of optionsRuntimeDecl.properties) {
|
|
||||||
if (
|
|
||||||
(prop.type === 'ObjectProperty' || prop.type === 'ObjectMethod') &&
|
|
||||||
prop.key.type === 'Identifier'
|
|
||||||
) {
|
|
||||||
if (prop.key.name === 'props') propsOption = prop
|
|
||||||
if (prop.key.name === 'emits') emitsOption = prop
|
|
||||||
if (prop.key.name === 'expose') exposeOption = prop
|
|
||||||
if (prop.key.name === 'slots') slotsOption = prop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (propsOption) {
|
|
||||||
error(
|
|
||||||
`${DEFINE_OPTIONS}() cannot be used to declare props. Use ${DEFINE_PROPS}() instead.`,
|
|
||||||
propsOption
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (emitsOption) {
|
|
||||||
error(
|
|
||||||
`${DEFINE_OPTIONS}() cannot be used to declare emits. Use ${DEFINE_EMITS}() instead.`,
|
|
||||||
emitsOption
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (exposeOption) {
|
|
||||||
error(
|
|
||||||
`${DEFINE_OPTIONS}() cannot be used to declare expose. Use ${DEFINE_EXPOSE}() instead.`,
|
|
||||||
exposeOption
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (slotsOption) {
|
|
||||||
error(
|
|
||||||
`${DEFINE_OPTIONS}() cannot be used to declare slots. Use ${DEFINE_SLOTS}() instead.`,
|
|
||||||
slotsOption
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
function processDefineExpose(node: Node): boolean {
|
|
||||||
if (isCallOf(node, DEFINE_EXPOSE)) {
|
|
||||||
if (ctx.hasDefineExposeCall) {
|
|
||||||
error(`duplicate ${DEFINE_EXPOSE}() call`, node)
|
|
||||||
}
|
|
||||||
ctx.hasDefineExposeCall = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkInvalidScopeReference(node: Node | undefined, method: string) {
|
function checkInvalidScopeReference(node: Node | undefined, method: string) {
|
||||||
if (!node) return
|
if (!node) return
|
||||||
walkIdentifiers(node, id => {
|
walkIdentifiers(node, id => {
|
||||||
const binding = setupBindings[id.name]
|
const binding = setupBindings[id.name]
|
||||||
if (binding && binding !== BindingTypes.LITERAL_CONST) {
|
if (binding && binding !== BindingTypes.LITERAL_CONST) {
|
||||||
error(
|
ctx.error(
|
||||||
`\`${method}()\` in <script setup> cannot reference locally ` +
|
`\`${method}()\` in <script setup> cannot reference locally ` +
|
||||||
`declared variables because it will be hoisted outside of the ` +
|
`declared variables because it will be hoisted outside of the ` +
|
||||||
`setup() function. If your component options require initialization ` +
|
`setup() function. If your component options require initialization ` +
|
||||||
|
@ -437,56 +323,6 @@ export function compileScript(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* await foo()
|
|
||||||
* -->
|
|
||||||
* ;(
|
|
||||||
* ([__temp,__restore] = withAsyncContext(() => foo())),
|
|
||||||
* await __temp,
|
|
||||||
* __restore()
|
|
||||||
* )
|
|
||||||
*
|
|
||||||
* const a = await foo()
|
|
||||||
* -->
|
|
||||||
* const a = (
|
|
||||||
* ([__temp, __restore] = withAsyncContext(() => foo())),
|
|
||||||
* __temp = await __temp,
|
|
||||||
* __restore(),
|
|
||||||
* __temp
|
|
||||||
* )
|
|
||||||
*/
|
|
||||||
function processAwait(
|
|
||||||
node: AwaitExpression,
|
|
||||||
needSemi: boolean,
|
|
||||||
isStatement: boolean
|
|
||||||
) {
|
|
||||||
const argumentStart =
|
|
||||||
node.argument.extra && node.argument.extra.parenthesized
|
|
||||||
? (node.argument.extra.parenStart as number)
|
|
||||||
: node.argument.start!
|
|
||||||
|
|
||||||
const argumentStr = source.slice(
|
|
||||||
argumentStart + startOffset,
|
|
||||||
node.argument.end! + startOffset
|
|
||||||
)
|
|
||||||
|
|
||||||
const containsNestedAwait = /\bawait\b/.test(argumentStr)
|
|
||||||
|
|
||||||
ctx.s.overwrite(
|
|
||||||
node.start! + startOffset,
|
|
||||||
argumentStart + startOffset,
|
|
||||||
`${needSemi ? `;` : ``}(\n ([__temp,__restore] = ${ctx.helper(
|
|
||||||
`withAsyncContext`
|
|
||||||
)}(${containsNestedAwait ? `async ` : ``}() => `
|
|
||||||
)
|
|
||||||
ctx.s.appendLeft(
|
|
||||||
node.end! + startOffset,
|
|
||||||
`)),\n ${isStatement ? `` : `__temp = `}await __temp,\n __restore()${
|
|
||||||
isStatement ? `` : `,\n __temp`
|
|
||||||
}\n)`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const scriptAst = ctx.scriptAst
|
const scriptAst = ctx.scriptAst
|
||||||
const scriptSetupAst = ctx.scriptSetupAst!
|
const scriptSetupAst = ctx.scriptSetupAst!
|
||||||
|
|
||||||
|
@ -556,7 +392,10 @@ export function compileScript(
|
||||||
// already imported in <script setup>, dedupe
|
// already imported in <script setup>, dedupe
|
||||||
removeSpecifier(i)
|
removeSpecifier(i)
|
||||||
} else {
|
} else {
|
||||||
error(`different imports aliased to same local name.`, specifier)
|
ctx.error(
|
||||||
|
`different imports aliased to same local name.`,
|
||||||
|
specifier
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
registerUserImport(
|
registerUserImport(
|
||||||
|
@ -725,7 +564,7 @@ export function compileScript(
|
||||||
node.label.name === 'ref' &&
|
node.label.name === 'ref' &&
|
||||||
node.body.type === 'ExpressionStatement'
|
node.body.type === 'ExpressionStatement'
|
||||||
) {
|
) {
|
||||||
error(
|
ctx.error(
|
||||||
`ref sugar using the label syntax was an experimental proposal and ` +
|
`ref sugar using the label syntax was an experimental proposal and ` +
|
||||||
`has been dropped based on community feedback. Please check out ` +
|
`has been dropped based on community feedback. Please check out ` +
|
||||||
`the new proposal at https://github.com/vuejs/rfcs/discussions/369`,
|
`the new proposal at https://github.com/vuejs/rfcs/discussions/369`,
|
||||||
|
@ -739,11 +578,11 @@ export function compileScript(
|
||||||
if (
|
if (
|
||||||
processDefineProps(ctx, expr) ||
|
processDefineProps(ctx, expr) ||
|
||||||
processDefineEmits(ctx, expr) ||
|
processDefineEmits(ctx, expr) ||
|
||||||
processDefineOptions(expr) ||
|
processDefineOptions(ctx, expr) ||
|
||||||
processDefineSlots(expr)
|
processDefineSlots(ctx, expr)
|
||||||
) {
|
) {
|
||||||
ctx.s.remove(node.start! + startOffset, node.end! + startOffset)
|
ctx.s.remove(node.start! + startOffset, node.end! + startOffset)
|
||||||
} else if (processDefineExpose(expr)) {
|
} else if (processDefineExpose(ctx, expr)) {
|
||||||
// defineExpose({}) -> expose({})
|
// defineExpose({}) -> expose({})
|
||||||
const callee = (expr as CallExpression).callee
|
const callee = (expr as CallExpression).callee
|
||||||
ctx.s.overwrite(
|
ctx.s.overwrite(
|
||||||
|
@ -765,8 +604,8 @@ export function compileScript(
|
||||||
const decl = node.declarations[i]
|
const decl = node.declarations[i]
|
||||||
const init = decl.init && unwrapTSNode(decl.init)
|
const init = decl.init && unwrapTSNode(decl.init)
|
||||||
if (init) {
|
if (init) {
|
||||||
if (processDefineOptions(init)) {
|
if (processDefineOptions(ctx, init)) {
|
||||||
error(
|
ctx.error(
|
||||||
`${DEFINE_OPTIONS}() has no returning value, it cannot be assigned.`,
|
`${DEFINE_OPTIONS}() has no returning value, it cannot be assigned.`,
|
||||||
node
|
node
|
||||||
)
|
)
|
||||||
|
@ -777,7 +616,7 @@ export function compileScript(
|
||||||
const isDefineEmits =
|
const isDefineEmits =
|
||||||
!isDefineProps && processDefineEmits(ctx, init, decl.id)
|
!isDefineProps && processDefineEmits(ctx, init, decl.id)
|
||||||
!isDefineEmits &&
|
!isDefineEmits &&
|
||||||
(processDefineSlots(init, decl.id) ||
|
(processDefineSlots(ctx, init, decl.id) ||
|
||||||
processDefineModel(ctx, init, decl.id))
|
processDefineModel(ctx, init, decl.id))
|
||||||
|
|
||||||
if (isDefineProps || isDefineEmits) {
|
if (isDefineProps || isDefineEmits) {
|
||||||
|
@ -858,6 +697,7 @@ export function compileScript(
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
processAwait(
|
processAwait(
|
||||||
|
ctx,
|
||||||
child,
|
child,
|
||||||
needsSemi,
|
needsSemi,
|
||||||
parent.type === 'ExpressionStatement'
|
parent.type === 'ExpressionStatement'
|
||||||
|
@ -875,7 +715,7 @@ export function compileScript(
|
||||||
node.type === 'ExportAllDeclaration' ||
|
node.type === 'ExportAllDeclaration' ||
|
||||||
node.type === 'ExportDefaultDeclaration'
|
node.type === 'ExportDefaultDeclaration'
|
||||||
) {
|
) {
|
||||||
error(
|
ctx.error(
|
||||||
`<script setup> cannot contain ES module exports. ` +
|
`<script setup> cannot contain ES module exports. ` +
|
||||||
`If you are using a previous version of <script setup>, please ` +
|
`If you are using a previous version of <script setup>, please ` +
|
||||||
`consult the updated RFC at https://github.com/vuejs/rfcs/pull/227.`,
|
`consult the updated RFC at https://github.com/vuejs/rfcs/pull/227.`,
|
||||||
|
@ -929,7 +769,7 @@ export function compileScript(
|
||||||
checkInvalidScopeReference(ctx.propsRuntimeDefaults, DEFINE_PROPS)
|
checkInvalidScopeReference(ctx.propsRuntimeDefaults, DEFINE_PROPS)
|
||||||
checkInvalidScopeReference(ctx.propsDestructureDecl, DEFINE_PROPS)
|
checkInvalidScopeReference(ctx.propsDestructureDecl, DEFINE_PROPS)
|
||||||
checkInvalidScopeReference(ctx.emitsRuntimeDecl, DEFINE_EMITS)
|
checkInvalidScopeReference(ctx.emitsRuntimeDecl, DEFINE_EMITS)
|
||||||
checkInvalidScopeReference(optionsRuntimeDecl, DEFINE_OPTIONS)
|
checkInvalidScopeReference(ctx.optionsRuntimeDecl, DEFINE_OPTIONS)
|
||||||
|
|
||||||
// 6. remove non-script content
|
// 6. remove non-script content
|
||||||
if (script) {
|
if (script) {
|
||||||
|
@ -1171,9 +1011,9 @@ export function compileScript(
|
||||||
if (emitsDecl) runtimeOptions += `\n emits: ${emitsDecl},`
|
if (emitsDecl) runtimeOptions += `\n emits: ${emitsDecl},`
|
||||||
|
|
||||||
let definedOptions = ''
|
let definedOptions = ''
|
||||||
if (optionsRuntimeDecl) {
|
if (ctx.optionsRuntimeDecl) {
|
||||||
definedOptions = scriptSetup.content
|
definedOptions = scriptSetup.content
|
||||||
.slice(optionsRuntimeDecl.start!, optionsRuntimeDecl.end!)
|
.slice(ctx.optionsRuntimeDecl.start!, ctx.optionsRuntimeDecl.end!)
|
||||||
.trim()
|
.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,6 @@ export class ScriptCompileContext {
|
||||||
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
|
|
||||||
scriptEndOffset = this.descriptor.script?.loc.end.offset
|
|
||||||
|
|
||||||
declaredTypes: Record<string, string[]> = Object.create(null)
|
declaredTypes: Record<string, string[]> = Object.create(null)
|
||||||
|
|
||||||
|
@ -51,6 +49,9 @@ export class ScriptCompileContext {
|
||||||
// defineModel
|
// defineModel
|
||||||
modelDecls: Record<string, ModelDecl> = {}
|
modelDecls: Record<string, ModelDecl> = {}
|
||||||
|
|
||||||
|
// defineOptions
|
||||||
|
optionsRuntimeDecl: Node | undefined
|
||||||
|
|
||||||
// codegen
|
// codegen
|
||||||
bindingMetadata: BindingMetadata = {}
|
bindingMetadata: BindingMetadata = {}
|
||||||
|
|
||||||
|
@ -125,7 +126,7 @@ export class ScriptCompileContext {
|
||||||
plugins,
|
plugins,
|
||||||
sourceType: 'module'
|
sourceType: 'module'
|
||||||
},
|
},
|
||||||
this.scriptStartOffset!
|
this.descriptor.script.loc.start.offset
|
||||||
)
|
)
|
||||||
|
|
||||||
this.scriptSetupAst =
|
this.scriptSetupAst =
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Node } from '@babel/types'
|
||||||
|
import { isCallOf } from './utils'
|
||||||
|
import { ScriptCompileContext } from './context'
|
||||||
|
|
||||||
|
export const DEFINE_EXPOSE = 'defineExpose'
|
||||||
|
|
||||||
|
export function processDefineExpose(
|
||||||
|
ctx: ScriptCompileContext,
|
||||||
|
node: Node
|
||||||
|
): boolean {
|
||||||
|
if (isCallOf(node, DEFINE_EXPOSE)) {
|
||||||
|
if (ctx.hasDefineExposeCall) {
|
||||||
|
ctx.error(`duplicate ${DEFINE_EXPOSE}() call`, node)
|
||||||
|
}
|
||||||
|
ctx.hasDefineExposeCall = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { Node } from '@babel/types'
|
||||||
|
import { ScriptCompileContext } from './context'
|
||||||
|
import { isCallOf, unwrapTSNode } from './utils'
|
||||||
|
import { DEFINE_PROPS } from './defineProps'
|
||||||
|
import { DEFINE_EMITS } from './defineEmits'
|
||||||
|
import { DEFINE_EXPOSE } from './defineExpose'
|
||||||
|
import { DEFINE_SLOTS } from './defineSlots'
|
||||||
|
|
||||||
|
export const DEFINE_OPTIONS = 'defineOptions'
|
||||||
|
|
||||||
|
export function processDefineOptions(
|
||||||
|
ctx: ScriptCompileContext,
|
||||||
|
node: Node
|
||||||
|
): boolean {
|
||||||
|
if (!isCallOf(node, DEFINE_OPTIONS)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (ctx.hasDefineOptionsCall) {
|
||||||
|
ctx.error(`duplicate ${DEFINE_OPTIONS}() call`, node)
|
||||||
|
}
|
||||||
|
if (node.typeParameters) {
|
||||||
|
ctx.error(`${DEFINE_OPTIONS}() cannot accept type arguments`, node)
|
||||||
|
}
|
||||||
|
if (!node.arguments[0]) return true
|
||||||
|
|
||||||
|
ctx.hasDefineOptionsCall = true
|
||||||
|
ctx.optionsRuntimeDecl = unwrapTSNode(node.arguments[0])
|
||||||
|
|
||||||
|
let propsOption = undefined
|
||||||
|
let emitsOption = undefined
|
||||||
|
let exposeOption = undefined
|
||||||
|
let slotsOption = undefined
|
||||||
|
if (ctx.optionsRuntimeDecl.type === 'ObjectExpression') {
|
||||||
|
for (const prop of ctx.optionsRuntimeDecl.properties) {
|
||||||
|
if (
|
||||||
|
(prop.type === 'ObjectProperty' || prop.type === 'ObjectMethod') &&
|
||||||
|
prop.key.type === 'Identifier'
|
||||||
|
) {
|
||||||
|
if (prop.key.name === 'props') propsOption = prop
|
||||||
|
if (prop.key.name === 'emits') emitsOption = prop
|
||||||
|
if (prop.key.name === 'expose') exposeOption = prop
|
||||||
|
if (prop.key.name === 'slots') slotsOption = prop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propsOption) {
|
||||||
|
ctx.error(
|
||||||
|
`${DEFINE_OPTIONS}() cannot be used to declare props. Use ${DEFINE_PROPS}() instead.`,
|
||||||
|
propsOption
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (emitsOption) {
|
||||||
|
ctx.error(
|
||||||
|
`${DEFINE_OPTIONS}() cannot be used to declare emits. Use ${DEFINE_EMITS}() instead.`,
|
||||||
|
emitsOption
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (exposeOption) {
|
||||||
|
ctx.error(
|
||||||
|
`${DEFINE_OPTIONS}() cannot be used to declare expose. Use ${DEFINE_EXPOSE}() instead.`,
|
||||||
|
exposeOption
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (slotsOption) {
|
||||||
|
ctx.error(
|
||||||
|
`${DEFINE_OPTIONS}() cannot be used to declare slots. Use ${DEFINE_SLOTS}() instead.`,
|
||||||
|
slotsOption
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { LVal, Node } from '@babel/types'
|
||||||
|
import { isCallOf } from './utils'
|
||||||
|
import { ScriptCompileContext } from './context'
|
||||||
|
|
||||||
|
export const DEFINE_SLOTS = 'defineSlots'
|
||||||
|
|
||||||
|
export function processDefineSlots(
|
||||||
|
ctx: ScriptCompileContext,
|
||||||
|
node: Node,
|
||||||
|
declId?: LVal
|
||||||
|
): boolean {
|
||||||
|
if (!isCallOf(node, DEFINE_SLOTS)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (ctx.hasDefineSlotsCall) {
|
||||||
|
ctx.error(`duplicate ${DEFINE_SLOTS}() call`, node)
|
||||||
|
}
|
||||||
|
ctx.hasDefineSlotsCall = true
|
||||||
|
|
||||||
|
if (node.arguments.length > 0) {
|
||||||
|
ctx.error(`${DEFINE_SLOTS}() cannot accept arguments`, node)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (declId) {
|
||||||
|
ctx.s.overwrite(
|
||||||
|
ctx.startOffset! + node.start!,
|
||||||
|
ctx.startOffset! + node.end!,
|
||||||
|
`${ctx.helper('useSlots')}()`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
|
@ -8,11 +8,6 @@ import {
|
||||||
import { FromNormalScript, UNKNOWN_TYPE } from './utils'
|
import { FromNormalScript, UNKNOWN_TYPE } from './utils'
|
||||||
import { ScriptCompileContext } from './context'
|
import { ScriptCompileContext } from './context'
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve a type Node into
|
|
||||||
*/
|
|
||||||
export function resolveType() {}
|
|
||||||
|
|
||||||
export function resolveQualifiedType(
|
export function resolveQualifiedType(
|
||||||
ctx: ScriptCompileContext,
|
ctx: ScriptCompileContext,
|
||||||
node: Node,
|
node: Node,
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
import { AwaitExpression } from '@babel/types'
|
||||||
|
import { ScriptCompileContext } from './context'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support context-persistence between top-level await expressions:
|
||||||
|
*
|
||||||
|
* ```js
|
||||||
|
* const instance = getCurrentInstance()
|
||||||
|
* await foo()
|
||||||
|
* expect(getCurrentInstance()).toBe(instance)
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* In the future we can potentially get rid of this when Async Context
|
||||||
|
* becomes generally available: https://github.com/tc39/proposal-async-context
|
||||||
|
*
|
||||||
|
* ```js
|
||||||
|
* // input
|
||||||
|
* await foo()
|
||||||
|
* // output
|
||||||
|
* ;(
|
||||||
|
* ([__temp,__restore] = withAsyncContext(() => foo())),
|
||||||
|
* await __temp,
|
||||||
|
* __restore()
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* // input
|
||||||
|
* const a = await foo()
|
||||||
|
* // output
|
||||||
|
* const a = (
|
||||||
|
* ([__temp, __restore] = withAsyncContext(() => foo())),
|
||||||
|
* __temp = await __temp,
|
||||||
|
* __restore(),
|
||||||
|
* __temp
|
||||||
|
* )
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function processAwait(
|
||||||
|
ctx: ScriptCompileContext,
|
||||||
|
node: AwaitExpression,
|
||||||
|
needSemi: boolean,
|
||||||
|
isStatement: boolean
|
||||||
|
) {
|
||||||
|
const argumentStart =
|
||||||
|
node.argument.extra && node.argument.extra.parenthesized
|
||||||
|
? (node.argument.extra.parenStart as number)
|
||||||
|
: node.argument.start!
|
||||||
|
|
||||||
|
const startOffset = ctx.startOffset!
|
||||||
|
const argumentStr = ctx.descriptor.source.slice(
|
||||||
|
argumentStart + startOffset,
|
||||||
|
node.argument.end! + startOffset
|
||||||
|
)
|
||||||
|
|
||||||
|
const containsNestedAwait = /\bawait\b/.test(argumentStr)
|
||||||
|
|
||||||
|
ctx.s.overwrite(
|
||||||
|
node.start! + startOffset,
|
||||||
|
argumentStart + startOffset,
|
||||||
|
`${needSemi ? `;` : ``}(\n ([__temp,__restore] = ${ctx.helper(
|
||||||
|
`withAsyncContext`
|
||||||
|
)}(${containsNestedAwait ? `async ` : ``}() => `
|
||||||
|
)
|
||||||
|
ctx.s.appendLeft(
|
||||||
|
node.end! + startOffset,
|
||||||
|
`)),\n ${isStatement ? `` : `__temp = `}await __temp,\n __restore()${
|
||||||
|
isStatement ? `` : `,\n __temp`
|
||||||
|
}\n)`
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue