feat(compiler-sfc): compileScript parseOnly mode
This is an internal feature meant for IDE support
This commit is contained in:
parent
96cc335aa7
commit
601a290caa
@ -0,0 +1,77 @@
|
||||
import { TextRange } from '../src/parse'
|
||||
import { compileSFCScript } from './utils'
|
||||
|
||||
describe('compileScript parseOnly mode', () => {
|
||||
function compile(src: string) {
|
||||
return compileSFCScript(src, { parseOnly: true })
|
||||
}
|
||||
|
||||
function getRange(src: string, range: TextRange) {
|
||||
return src.slice(range.start, range.end)
|
||||
}
|
||||
|
||||
test('bindings', () => {
|
||||
const scriptSrc = `
|
||||
import { foo } from './x'
|
||||
`
|
||||
const scriptSetupSrc = `
|
||||
import { bar } from './x'
|
||||
|
||||
const a = 123
|
||||
function b() {}
|
||||
class c {}
|
||||
`
|
||||
const src = `
|
||||
<script>${scriptSrc}</script>
|
||||
<script setup>${scriptSetupSrc}</script>
|
||||
`
|
||||
const { ranges } = compile(src)
|
||||
|
||||
expect(getRange(scriptSrc, ranges!.scriptBindings[0])).toBe('foo')
|
||||
expect(
|
||||
ranges!.scriptSetupBindings.map(r => getRange(scriptSetupSrc, r))
|
||||
).toMatchObject(['bar', 'a', 'b', 'c'])
|
||||
})
|
||||
|
||||
test('defineProps', () => {
|
||||
const src = `
|
||||
defineProps({ foo: String })
|
||||
`
|
||||
const { ranges } = compile(`<script setup>${src}</script>`)
|
||||
expect(getRange(src, ranges!.propsRuntimeArg!)).toBe(`{ foo: String }`)
|
||||
})
|
||||
|
||||
test('defineProps (type)', () => {
|
||||
const src = `
|
||||
interface Props { x?: number }
|
||||
defineProps<Props>()
|
||||
`
|
||||
const { ranges } = compile(`<script setup lang="ts">${src}</script>`)
|
||||
expect(getRange(src, ranges!.propsTypeArg!)).toBe(`Props`)
|
||||
})
|
||||
|
||||
test('withDefaults', () => {
|
||||
const src = `
|
||||
interface Props { x?: number }
|
||||
withDefaults(defineProps<Props>(), { x: 1 })
|
||||
`
|
||||
const { ranges } = compile(`<script setup lang="ts">${src}</script>`)
|
||||
expect(getRange(src, ranges!.withDefaultsArg!)).toBe(`{ x: 1 }`)
|
||||
})
|
||||
|
||||
test('defineEmits', () => {
|
||||
const src = `
|
||||
defineEmits(['foo'])
|
||||
`
|
||||
const { ranges } = compile(`<script setup>${src}</script>`)
|
||||
expect(getRange(src, ranges!.emitsRuntimeArg!)).toBe(`['foo']`)
|
||||
})
|
||||
|
||||
test('defineEmits (type)', () => {
|
||||
const src = `
|
||||
defineEmits<{ (e: 'x'): void }>()
|
||||
`
|
||||
const { ranges } = compile(`<script setup lang="ts">${src}</script>`)
|
||||
expect(getRange(src, ranges!.emitsTypeArg!)).toBe(`{ (e: 'x'): void }`)
|
||||
})
|
||||
})
|
@ -1,6 +1,11 @@
|
||||
import MagicString from 'magic-string'
|
||||
import { BindingMetadata, BindingTypes, UNREF } from '@vue/compiler-core'
|
||||
import { SFCDescriptor, SFCScriptBlock } from './parse'
|
||||
import {
|
||||
ScriptSetupTextRanges,
|
||||
SFCDescriptor,
|
||||
SFCScriptBlock,
|
||||
TextRange
|
||||
} from './parse'
|
||||
import { parse as _parse, ParserOptions, ParserPlugin } from '@babel/parser'
|
||||
import { babelParserDefaultPlugins, generateCodeFrame } from '@vue/shared'
|
||||
import {
|
||||
@ -71,7 +76,31 @@ export interface SFCScriptCompileOptions {
|
||||
* from being hot-reloaded separately from component state.
|
||||
*/
|
||||
inlineTemplate?: boolean
|
||||
/**
|
||||
* Options for template compilation when inlining. Note these are options that
|
||||
* would normally be pased to `compiler-sfc`'s own `compileTemplate()`, not
|
||||
* options passed to `compiler-dom`.
|
||||
*/
|
||||
templateOptions?: Partial<SFCTemplateCompileOptions>
|
||||
/**
|
||||
* Skip codegen and only return AST / binding / text range information.
|
||||
* Also makes the call error-tolerant.
|
||||
* Used for IDE support.
|
||||
*/
|
||||
parseOnly?: boolean
|
||||
}
|
||||
|
||||
interface ImportBinding {
|
||||
isType: boolean
|
||||
imported: string
|
||||
source: string
|
||||
rangeNode: Node
|
||||
isFromSetup: boolean
|
||||
}
|
||||
|
||||
interface VariableBinding {
|
||||
type: BindingTypes
|
||||
rangeNode: Node
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,10 +112,22 @@ export function compileScript(
|
||||
sfc: SFCDescriptor,
|
||||
options: SFCScriptCompileOptions
|
||||
): SFCScriptBlock {
|
||||
const { script, scriptSetup, source, filename } = sfc
|
||||
let { script, scriptSetup, source, filename } = sfc
|
||||
// feature flags
|
||||
const enableRefSugar = !!options.refSugar
|
||||
const parseOnly = !!options.parseOnly
|
||||
|
||||
if (scriptSetup) {
|
||||
warnExperimental(`<script setup>`, 227)
|
||||
!parseOnly && warnExperimental(`<script setup>`, 227)
|
||||
} else if (parseOnly) {
|
||||
// in parse-only mode, construct a fake script setup so we still perform
|
||||
// the full parse logic.
|
||||
scriptSetup = {
|
||||
type: 'script',
|
||||
content: '',
|
||||
attrs: {},
|
||||
loc: null as any
|
||||
}
|
||||
}
|
||||
|
||||
// for backwards compat
|
||||
@ -134,7 +175,8 @@ export function compileScript(
|
||||
try {
|
||||
const scriptAst = _parse(script.content, {
|
||||
plugins,
|
||||
sourceType: 'module'
|
||||
sourceType: 'module',
|
||||
errorRecovery: parseOnly
|
||||
}).program.body
|
||||
const bindings = analyzeScriptBindings(scriptAst)
|
||||
let content = script.content
|
||||
@ -165,7 +207,8 @@ export function compileScript(
|
||||
|
||||
if (script && scriptLang !== scriptSetupLang) {
|
||||
throw new Error(
|
||||
`[@vue/compiler-sfc] <script> and <script setup> must have the same language type.`
|
||||
`[@vue/compiler-sfc] <script> and <script setup> must have the same ` +
|
||||
`language type.`
|
||||
)
|
||||
}
|
||||
|
||||
@ -174,22 +217,22 @@ export function compileScript(
|
||||
return scriptSetup
|
||||
}
|
||||
|
||||
const defaultTempVar = `__default__`
|
||||
// metadata that needs to be returned
|
||||
const bindingMetadata: BindingMetadata = {}
|
||||
const helperImports: Set<string> = new Set()
|
||||
const userImports: Record<
|
||||
string,
|
||||
{
|
||||
isType: boolean
|
||||
imported: string
|
||||
source: string
|
||||
const ranges: ScriptSetupTextRanges | undefined = parseOnly
|
||||
? {
|
||||
scriptBindings: [],
|
||||
scriptSetupBindings: []
|
||||
}
|
||||
> = Object.create(null)
|
||||
: undefined
|
||||
|
||||
const defaultTempVar = `__default__`
|
||||
const helperImports: Set<string> = new Set()
|
||||
const userImports: Record<string, ImportBinding> = Object.create(null)
|
||||
const userImportAlias: Record<string, string> = Object.create(null)
|
||||
const setupBindings: Record<string, BindingTypes> = Object.create(null)
|
||||
const refBindings: Record<string, BindingTypes> = Object.create(null)
|
||||
const setupBindings: Record<string, VariableBinding> = Object.create(null)
|
||||
const refBindings: Record<string, VariableBinding> = Object.create(null)
|
||||
const refIdentifiers: Set<Identifier> = new Set()
|
||||
const enableRefSugar = !!options.refSugar
|
||||
let defaultExport: Node | undefined
|
||||
let hasDefinePropsCall = false
|
||||
let hasDefineEmitCall = false
|
||||
@ -197,9 +240,15 @@ export function compileScript(
|
||||
let propsRuntimeDecl: Node | undefined
|
||||
let propsRuntimeDefaults: Node | undefined
|
||||
let propsTypeDecl: TSTypeLiteral | TSInterfaceBody | undefined
|
||||
let propsTypeDeclRaw: Node | undefined
|
||||
let propsIdentifier: string | undefined
|
||||
let emitRuntimeDecl: Node | undefined
|
||||
let emitTypeDecl: TSFunctionType | TSTypeLiteral | TSInterfaceBody | undefined
|
||||
let emitsRuntimeDecl: Node | undefined
|
||||
let emitsTypeDecl:
|
||||
| TSFunctionType
|
||||
| TSTypeLiteral
|
||||
| TSInterfaceBody
|
||||
| undefined
|
||||
let emitsTypeDeclRaw: Node | undefined
|
||||
let emitIdentifier: string | undefined
|
||||
let hasAwait = false
|
||||
let hasInlinedSsrRenderFn = false
|
||||
@ -227,6 +276,7 @@ export function compileScript(
|
||||
offset: number
|
||||
): Statement[] {
|
||||
try {
|
||||
options.errorRecovery = parseOnly
|
||||
return _parse(input, options).program.body
|
||||
} catch (e) {
|
||||
e.message = `[@vue/compiler-sfc] ${e.message}\n\n${
|
||||
@ -254,7 +304,9 @@ export function compileScript(
|
||||
source: string,
|
||||
local: string,
|
||||
imported: string | false,
|
||||
isType: boolean
|
||||
isType: boolean,
|
||||
isFromSetup: boolean,
|
||||
rangeNode: Node
|
||||
) {
|
||||
if (source === 'vue' && imported) {
|
||||
userImportAlias[imported] = local
|
||||
@ -262,7 +314,9 @@ export function compileScript(
|
||||
userImports[local] = {
|
||||
isType,
|
||||
imported: imported || 'default',
|
||||
source
|
||||
source,
|
||||
rangeNode,
|
||||
isFromSetup
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,8 +342,9 @@ export function compileScript(
|
||||
)
|
||||
}
|
||||
|
||||
propsTypeDeclRaw = node.typeParameters.params[0]
|
||||
propsTypeDecl = resolveQualifiedType(
|
||||
node.typeParameters.params[0],
|
||||
propsTypeDeclRaw,
|
||||
node => node.type === 'TSTypeLiteral'
|
||||
) as TSTypeLiteral | TSInterfaceBody | undefined
|
||||
|
||||
@ -297,7 +352,7 @@ export function compileScript(
|
||||
error(
|
||||
`type argument passed to ${DEFINE_PROPS}() must be a literal type, ` +
|
||||
`or a reference to an interface or literal type.`,
|
||||
node.typeParameters.params[0]
|
||||
propsTypeDeclRaw
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -335,9 +390,9 @@ export function compileScript(
|
||||
error(`duplicate ${DEFINE_EMITS}() call`, node)
|
||||
}
|
||||
hasDefineEmitCall = true
|
||||
emitRuntimeDecl = node.arguments[0]
|
||||
emitsRuntimeDecl = node.arguments[0]
|
||||
if (node.typeParameters) {
|
||||
if (emitRuntimeDecl) {
|
||||
if (emitsRuntimeDecl) {
|
||||
error(
|
||||
`${DEFINE_EMIT}() cannot accept both type and non-type arguments ` +
|
||||
`at the same time. Use one or the other.`,
|
||||
@ -345,16 +400,17 @@ export function compileScript(
|
||||
)
|
||||
}
|
||||
|
||||
emitTypeDecl = resolveQualifiedType(
|
||||
node.typeParameters.params[0],
|
||||
emitsTypeDeclRaw = node.typeParameters.params[0]
|
||||
emitsTypeDecl = resolveQualifiedType(
|
||||
emitsTypeDeclRaw,
|
||||
node => node.type === 'TSFunctionType' || node.type === 'TSTypeLiteral'
|
||||
) as TSFunctionType | TSTypeLiteral | TSInterfaceBody | undefined
|
||||
|
||||
if (!emitTypeDecl) {
|
||||
if (!emitsTypeDecl) {
|
||||
error(
|
||||
`type argument passed to ${DEFINE_EMITS}() must be a function type, ` +
|
||||
`a literal type with call signatures, or a reference to the above types.`,
|
||||
node.typeParameters.params[0]
|
||||
emitsTypeDeclRaw
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -469,7 +525,10 @@ export function compileScript(
|
||||
if (id.name[0] === '$') {
|
||||
error(`ref variable identifiers cannot start with $.`, id)
|
||||
}
|
||||
refBindings[id.name] = setupBindings[id.name] = BindingTypes.SETUP_REF
|
||||
refBindings[id.name] = setupBindings[id.name] = {
|
||||
type: BindingTypes.SETUP_REF,
|
||||
rangeNode: id
|
||||
}
|
||||
refIdentifiers.add(id)
|
||||
}
|
||||
|
||||
@ -635,7 +694,9 @@ export function compileScript(
|
||||
node.source.value,
|
||||
specifier.local.name,
|
||||
imported,
|
||||
node.importKind === 'type'
|
||||
node.importKind === 'type',
|
||||
false,
|
||||
specifier.local
|
||||
)
|
||||
}
|
||||
} else if (node.type === 'ExportDefaultDeclaration') {
|
||||
@ -724,7 +785,7 @@ export function compileScript(
|
||||
node.body.type === 'ExpressionStatement'
|
||||
) {
|
||||
if (enableRefSugar) {
|
||||
warnExperimental(`ref: sugar`, 228)
|
||||
!parseOnly && warnExperimental(`ref: sugar`, 228)
|
||||
s.overwrite(
|
||||
node.label.start! + startOffset,
|
||||
node.body.start! + startOffset,
|
||||
@ -795,7 +856,9 @@ export function compileScript(
|
||||
source,
|
||||
local,
|
||||
imported,
|
||||
node.importKind === 'type'
|
||||
node.importKind === 'type',
|
||||
true,
|
||||
specifier.local
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -925,6 +988,44 @@ export function compileScript(
|
||||
}
|
||||
}
|
||||
|
||||
// in parse only mode, we should have collected all the information we need,
|
||||
// return early.
|
||||
if (parseOnly) {
|
||||
for (const key in userImports) {
|
||||
const { rangeNode, isFromSetup } = userImports[key]
|
||||
const bindings = isFromSetup
|
||||
? ranges!.scriptSetupBindings
|
||||
: ranges!.scriptBindings
|
||||
bindings.push(toTextRange(rangeNode))
|
||||
}
|
||||
for (const key in setupBindings) {
|
||||
ranges!.scriptSetupBindings.push(
|
||||
toTextRange(setupBindings[key].rangeNode)
|
||||
)
|
||||
}
|
||||
if (propsRuntimeDecl) {
|
||||
ranges!.propsRuntimeArg = toTextRange(propsRuntimeDecl)
|
||||
}
|
||||
if (propsTypeDeclRaw) {
|
||||
ranges!.propsTypeArg = toTextRange(propsTypeDeclRaw)
|
||||
}
|
||||
if (emitsRuntimeDecl) {
|
||||
ranges!.emitsRuntimeArg = toTextRange(emitsRuntimeDecl)
|
||||
}
|
||||
if (emitsTypeDeclRaw) {
|
||||
ranges!.emitsTypeArg = toTextRange(emitsTypeDeclRaw)
|
||||
}
|
||||
if (propsRuntimeDefaults) {
|
||||
ranges!.withDefaultsArg = toTextRange(propsRuntimeDefaults)
|
||||
}
|
||||
return {
|
||||
...scriptSetup,
|
||||
ranges,
|
||||
scriptAst,
|
||||
scriptSetupAst
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Do a full walk to rewrite identifiers referencing let exports with ref
|
||||
// value access
|
||||
if (enableRefSugar && Object.keys(refBindings).length) {
|
||||
@ -958,15 +1059,15 @@ export function compileScript(
|
||||
if (propsTypeDecl) {
|
||||
extractRuntimeProps(propsTypeDecl, typeDeclaredProps, declaredTypes)
|
||||
}
|
||||
if (emitTypeDecl) {
|
||||
extractRuntimeEmits(emitTypeDecl, typeDeclaredEmits)
|
||||
if (emitsTypeDecl) {
|
||||
extractRuntimeEmits(emitsTypeDecl, typeDeclaredEmits)
|
||||
}
|
||||
|
||||
// 5. check useOptions args to make sure it doesn't reference setup scope
|
||||
// variables
|
||||
checkInvalidScopeReference(propsRuntimeDecl, DEFINE_PROPS)
|
||||
checkInvalidScopeReference(propsRuntimeDefaults, DEFINE_PROPS)
|
||||
checkInvalidScopeReference(emitRuntimeDecl, DEFINE_PROPS)
|
||||
checkInvalidScopeReference(emitsRuntimeDecl, DEFINE_PROPS)
|
||||
|
||||
// 6. remove non-script content
|
||||
if (script) {
|
||||
@ -1009,7 +1110,7 @@ export function compileScript(
|
||||
: BindingTypes.SETUP_MAYBE_REF
|
||||
}
|
||||
for (const key in setupBindings) {
|
||||
bindingMetadata[key] = setupBindings[key]
|
||||
bindingMetadata[key] = setupBindings[key].type
|
||||
}
|
||||
|
||||
// 8. inject `useCssVars` calls
|
||||
@ -1051,10 +1152,10 @@ export function compileScript(
|
||||
}
|
||||
if (destructureElements.length) {
|
||||
args += `, { ${destructureElements.join(', ')} }`
|
||||
if (emitTypeDecl) {
|
||||
if (emitsTypeDecl) {
|
||||
args += `: { emit: (${scriptSetup.content.slice(
|
||||
emitTypeDecl.start!,
|
||||
emitTypeDecl.end!
|
||||
emitsTypeDecl.start!,
|
||||
emitsTypeDecl.end!
|
||||
)}), expose: any, slots: any, attrs: any }`
|
||||
}
|
||||
}
|
||||
@ -1156,11 +1257,11 @@ export function compileScript(
|
||||
} else if (propsTypeDecl) {
|
||||
runtimeOptions += genRuntimeProps(typeDeclaredProps)
|
||||
}
|
||||
if (emitRuntimeDecl) {
|
||||
if (emitsRuntimeDecl) {
|
||||
runtimeOptions += `\n emits: ${scriptSetup.content
|
||||
.slice(emitRuntimeDecl.start!, emitRuntimeDecl.end!)
|
||||
.slice(emitsRuntimeDecl.start!, emitsRuntimeDecl.end!)
|
||||
.trim()},`
|
||||
} else if (emitTypeDecl) {
|
||||
} else if (emitsTypeDecl) {
|
||||
runtimeOptions += genRuntimeEmits(typeDeclaredEmits)
|
||||
}
|
||||
|
||||
@ -1230,9 +1331,20 @@ export function compileScript(
|
||||
}
|
||||
}
|
||||
|
||||
function registerBinding(
|
||||
bindings: Record<string, VariableBinding>,
|
||||
node: Identifier,
|
||||
type: BindingTypes
|
||||
) {
|
||||
bindings[node.name] = {
|
||||
type,
|
||||
rangeNode: node
|
||||
}
|
||||
}
|
||||
|
||||
function walkDeclaration(
|
||||
node: Declaration,
|
||||
bindings: Record<string, BindingTypes>,
|
||||
bindings: Record<string, VariableBinding>,
|
||||
userImportAlias: Record<string, string>
|
||||
) {
|
||||
if (node.type === 'VariableDeclaration') {
|
||||
@ -1272,7 +1384,7 @@ function walkDeclaration(
|
||||
} else {
|
||||
bindingType = BindingTypes.SETUP_LET
|
||||
}
|
||||
bindings[id.name] = bindingType
|
||||
registerBinding(bindings, id, bindingType)
|
||||
} else if (id.type === 'ObjectPattern') {
|
||||
walkObjectPattern(id, bindings, isConst, isDefineCall)
|
||||
} else if (id.type === 'ArrayPattern') {
|
||||
@ -1285,13 +1397,16 @@ function walkDeclaration(
|
||||
) {
|
||||
// export function foo() {} / export class Foo {}
|
||||
// export declarations must be named.
|
||||
bindings[node.id!.name] = BindingTypes.SETUP_CONST
|
||||
bindings[node.id!.name] = {
|
||||
type: BindingTypes.SETUP_CONST,
|
||||
rangeNode: node.id!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function walkObjectPattern(
|
||||
node: ObjectPattern,
|
||||
bindings: Record<string, BindingTypes>,
|
||||
bindings: Record<string, VariableBinding>,
|
||||
isConst: boolean,
|
||||
isDefineCall = false
|
||||
) {
|
||||
@ -1301,11 +1416,12 @@ function walkObjectPattern(
|
||||
if (p.key.type === 'Identifier') {
|
||||
if (p.key === p.value) {
|
||||
// const { x } = ...
|
||||
bindings[p.key.name] = isDefineCall
|
||||
const type = isDefineCall
|
||||
? BindingTypes.SETUP_CONST
|
||||
: isConst
|
||||
? BindingTypes.SETUP_MAYBE_REF
|
||||
: BindingTypes.SETUP_LET
|
||||
registerBinding(bindings, p.key, type)
|
||||
} else {
|
||||
walkPattern(p.value, bindings, isConst, isDefineCall)
|
||||
}
|
||||
@ -1313,16 +1429,15 @@ function walkObjectPattern(
|
||||
} else {
|
||||
// ...rest
|
||||
// argument can only be identifer when destructuring
|
||||
bindings[(p.argument as Identifier).name] = isConst
|
||||
? BindingTypes.SETUP_CONST
|
||||
: BindingTypes.SETUP_LET
|
||||
const type = isConst ? BindingTypes.SETUP_CONST : BindingTypes.SETUP_LET
|
||||
registerBinding(bindings, p.argument as Identifier, type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function walkArrayPattern(
|
||||
node: ArrayPattern,
|
||||
bindings: Record<string, BindingTypes>,
|
||||
bindings: Record<string, VariableBinding>,
|
||||
isConst: boolean,
|
||||
isDefineCall = false
|
||||
) {
|
||||
@ -1333,32 +1448,33 @@ function walkArrayPattern(
|
||||
|
||||
function walkPattern(
|
||||
node: Node,
|
||||
bindings: Record<string, BindingTypes>,
|
||||
bindings: Record<string, VariableBinding>,
|
||||
isConst: boolean,
|
||||
isDefineCall = false
|
||||
) {
|
||||
if (node.type === 'Identifier') {
|
||||
bindings[node.name] = isDefineCall
|
||||
const type = isDefineCall
|
||||
? BindingTypes.SETUP_CONST
|
||||
: isConst
|
||||
? BindingTypes.SETUP_MAYBE_REF
|
||||
: BindingTypes.SETUP_LET
|
||||
registerBinding(bindings, node, type)
|
||||
} else if (node.type === 'RestElement') {
|
||||
// argument can only be identifer when destructuring
|
||||
bindings[(node.argument as Identifier).name] = isConst
|
||||
? BindingTypes.SETUP_CONST
|
||||
: BindingTypes.SETUP_LET
|
||||
const type = isConst ? BindingTypes.SETUP_CONST : BindingTypes.SETUP_LET
|
||||
registerBinding(bindings, node.argument as Identifier, type)
|
||||
} else if (node.type === 'ObjectPattern') {
|
||||
walkObjectPattern(node, bindings, isConst)
|
||||
} else if (node.type === 'ArrayPattern') {
|
||||
walkArrayPattern(node, bindings, isConst)
|
||||
} else if (node.type === 'AssignmentPattern') {
|
||||
if (node.left.type === 'Identifier') {
|
||||
bindings[node.left.name] = isDefineCall
|
||||
const type = isDefineCall
|
||||
? BindingTypes.SETUP_CONST
|
||||
: isConst
|
||||
? BindingTypes.SETUP_MAYBE_REF
|
||||
: BindingTypes.SETUP_LET
|
||||
registerBinding(bindings, node.left, type)
|
||||
} else {
|
||||
walkPattern(node.left, bindings, isConst)
|
||||
}
|
||||
@ -1958,3 +2074,10 @@ function extractIdentifiers(
|
||||
|
||||
return nodes
|
||||
}
|
||||
|
||||
function toTextRange(node: Node): TextRange {
|
||||
return {
|
||||
start: node.start!,
|
||||
end: node.end!
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,25 @@ export interface SFCScriptBlock extends SFCBlock {
|
||||
bindings?: BindingMetadata
|
||||
scriptAst?: Statement[]
|
||||
scriptSetupAst?: Statement[]
|
||||
ranges?: ScriptSetupTextRanges
|
||||
}
|
||||
|
||||
/**
|
||||
* Text range data for IDE support
|
||||
*/
|
||||
export interface ScriptSetupTextRanges {
|
||||
scriptBindings: TextRange[]
|
||||
scriptSetupBindings: TextRange[]
|
||||
propsTypeArg?: TextRange
|
||||
propsRuntimeArg?: TextRange
|
||||
emitsTypeArg?: TextRange
|
||||
emitsRuntimeArg?: TextRange
|
||||
withDefaultsArg?: TextRange
|
||||
}
|
||||
|
||||
export interface TextRange {
|
||||
start: number
|
||||
end: number
|
||||
}
|
||||
|
||||
export interface SFCStyleBlock extends SFCBlock {
|
||||
|
Loading…
Reference in New Issue
Block a user