refactor(compiler-sfc): remove parseOnly mode
This commit is contained in:
parent
db8dc753c0
commit
0c2ea1c134
@ -1,87 +0,0 @@
|
||||
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 }`)
|
||||
})
|
||||
|
||||
test('no script setup block', () => {
|
||||
const src = `import { x } from './x'`
|
||||
const { ranges } = compile(`<script>${src}</script>`)
|
||||
expect(getRange(src, ranges!.scriptBindings[0])).toBe(`x`)
|
||||
})
|
||||
|
||||
test('no script block', () => {
|
||||
expect(() => compile(`<style>hello</style>`)).not.toThrow()
|
||||
})
|
||||
})
|
@ -3,7 +3,6 @@ import {
|
||||
BindingMetadata,
|
||||
BindingTypes,
|
||||
createRoot,
|
||||
locStub,
|
||||
NodeTypes,
|
||||
transform,
|
||||
parserOptions,
|
||||
@ -12,12 +11,7 @@ import {
|
||||
isFunctionType,
|
||||
walkIdentifiers
|
||||
} from '@vue/compiler-dom'
|
||||
import {
|
||||
ScriptSetupTextRanges,
|
||||
SFCDescriptor,
|
||||
SFCScriptBlock,
|
||||
TextRange
|
||||
} from './parse'
|
||||
import { SFCDescriptor, SFCScriptBlock } from './parse'
|
||||
import { parse as _parse, ParserOptions, ParserPlugin } from '@babel/parser'
|
||||
import {
|
||||
babelParserDefaultPlugins,
|
||||
@ -103,28 +97,16 @@ export interface SFCScriptCompileOptions {
|
||||
* 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
|
||||
isUsedInTemplate: boolean
|
||||
}
|
||||
|
||||
interface VariableBinding {
|
||||
type: BindingTypes
|
||||
rangeNode: Node
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile `<script setup>`
|
||||
* It requires the whole SFC descriptor because we need to handle and merge
|
||||
@ -138,18 +120,6 @@ export function compileScript(
|
||||
// feature flags
|
||||
const enableRefSugar = !!options.refSugar
|
||||
let refBindings: string[] | undefined
|
||||
const parseOnly = !!options.parseOnly
|
||||
|
||||
if (parseOnly && !scriptSetup) {
|
||||
// in parse-only mode, construct a fake script setup so we still perform
|
||||
// the full parse logic.
|
||||
scriptSetup = {
|
||||
type: 'script',
|
||||
content: '',
|
||||
attrs: {},
|
||||
loc: locStub
|
||||
}
|
||||
}
|
||||
|
||||
// for backwards compat
|
||||
if (!options) {
|
||||
@ -190,8 +160,7 @@ export function compileScript(
|
||||
try {
|
||||
const scriptAst = _parse(script.content, {
|
||||
plugins,
|
||||
sourceType: 'module',
|
||||
errorRecovery: parseOnly
|
||||
sourceType: 'module'
|
||||
}).program.body
|
||||
const bindings = analyzeScriptBindings(scriptAst)
|
||||
let content = script.content
|
||||
@ -232,18 +201,11 @@ export function compileScript(
|
||||
|
||||
// metadata that needs to be returned
|
||||
const bindingMetadata: BindingMetadata = {}
|
||||
const ranges: ScriptSetupTextRanges | undefined = parseOnly
|
||||
? {
|
||||
scriptBindings: [],
|
||||
scriptSetupBindings: []
|
||||
}
|
||||
: 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, VariableBinding> = Object.create(null)
|
||||
const setupBindings: Record<string, BindingTypes> = Object.create(null)
|
||||
|
||||
let defaultExport: Node | undefined
|
||||
let hasDefinePropsCall = false
|
||||
@ -288,7 +250,6 @@ export function compileScript(
|
||||
offset: number
|
||||
): Program {
|
||||
try {
|
||||
options.errorRecovery = parseOnly
|
||||
return _parse(input, options).program
|
||||
} catch (e) {
|
||||
e.message = `[@vue/compiler-sfc] ${e.message}\n\n${
|
||||
@ -317,8 +278,7 @@ export function compileScript(
|
||||
local: string,
|
||||
imported: string | false,
|
||||
isType: boolean,
|
||||
isFromSetup: boolean,
|
||||
rangeNode: Node
|
||||
isFromSetup: boolean
|
||||
) {
|
||||
if (source === 'vue' && imported) {
|
||||
userImportAlias[imported] = local
|
||||
@ -337,7 +297,6 @@ export function compileScript(
|
||||
isType,
|
||||
imported: imported || 'default',
|
||||
source,
|
||||
rangeNode,
|
||||
isFromSetup,
|
||||
isUsedInTemplate
|
||||
}
|
||||
@ -609,8 +568,7 @@ export function compileScript(
|
||||
specifier.local.name,
|
||||
imported,
|
||||
node.importKind === 'type',
|
||||
false,
|
||||
specifier.local
|
||||
false
|
||||
)
|
||||
}
|
||||
} else if (node.type === 'ExportDefaultDeclaration') {
|
||||
@ -705,8 +663,8 @@ export function compileScript(
|
||||
) {
|
||||
error(
|
||||
`ref sugar using the label syntax was an experimental proposal and ` +
|
||||
`has been dropped based on community feedback.`,
|
||||
// TODO + ` Please check out the adjusted proposal at ...`,
|
||||
`has been dropped based on community feedback. Please check out ` +
|
||||
`the new proposal at https://github.com/vuejs/rfcs/discussions/369`,
|
||||
node
|
||||
)
|
||||
}
|
||||
@ -764,8 +722,7 @@ export function compileScript(
|
||||
local,
|
||||
imported,
|
||||
node.importKind === 'type',
|
||||
true,
|
||||
specifier.local
|
||||
true
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -897,44 +854,6 @@ 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: scriptAst?.body,
|
||||
scriptSetupAst: scriptSetupAst?.body
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Apply ref sugar transform
|
||||
if (enableRefSugar) {
|
||||
warnExperimental(
|
||||
@ -1007,7 +926,7 @@ export function compileScript(
|
||||
: BindingTypes.SETUP_MAYBE_REF
|
||||
}
|
||||
for (const key in setupBindings) {
|
||||
bindingMetadata[key] = setupBindings[key].type
|
||||
bindingMetadata[key] = setupBindings[key]
|
||||
}
|
||||
// known ref bindings
|
||||
if (refBindings) {
|
||||
@ -1240,19 +1159,16 @@ export function compileScript(
|
||||
}
|
||||
|
||||
function registerBinding(
|
||||
bindings: Record<string, VariableBinding>,
|
||||
bindings: Record<string, BindingTypes>,
|
||||
node: Identifier,
|
||||
type: BindingTypes
|
||||
) {
|
||||
bindings[node.name] = {
|
||||
type,
|
||||
rangeNode: node
|
||||
}
|
||||
bindings[node.name] = type
|
||||
}
|
||||
|
||||
function walkDeclaration(
|
||||
node: Declaration,
|
||||
bindings: Record<string, VariableBinding>,
|
||||
bindings: Record<string, BindingTypes>,
|
||||
userImportAlias: Record<string, string>
|
||||
) {
|
||||
if (node.type === 'VariableDeclaration') {
|
||||
@ -1301,16 +1217,13 @@ function walkDeclaration(
|
||||
) {
|
||||
// export function foo() {} / export class Foo {}
|
||||
// export declarations must be named.
|
||||
bindings[node.id!.name] = {
|
||||
type: BindingTypes.SETUP_CONST,
|
||||
rangeNode: node.id!
|
||||
}
|
||||
bindings[node.id!.name] = BindingTypes.SETUP_CONST
|
||||
}
|
||||
}
|
||||
|
||||
function walkObjectPattern(
|
||||
node: ObjectPattern,
|
||||
bindings: Record<string, VariableBinding>,
|
||||
bindings: Record<string, BindingTypes>,
|
||||
isConst: boolean,
|
||||
isDefineCall = false
|
||||
) {
|
||||
@ -1341,7 +1254,7 @@ function walkObjectPattern(
|
||||
|
||||
function walkArrayPattern(
|
||||
node: ArrayPattern,
|
||||
bindings: Record<string, VariableBinding>,
|
||||
bindings: Record<string, BindingTypes>,
|
||||
isConst: boolean,
|
||||
isDefineCall = false
|
||||
) {
|
||||
@ -1352,7 +1265,7 @@ function walkArrayPattern(
|
||||
|
||||
function walkPattern(
|
||||
node: Node,
|
||||
bindings: Record<string, VariableBinding>,
|
||||
bindings: Record<string, BindingTypes>,
|
||||
isConst: boolean,
|
||||
isDefineCall = false
|
||||
) {
|
||||
@ -1745,13 +1658,6 @@ function getObjectOrArrayExpressionKeys(value: Node): string[] {
|
||||
return []
|
||||
}
|
||||
|
||||
function toTextRange(node: Node): TextRange {
|
||||
return {
|
||||
start: node.start!,
|
||||
end: node.end!
|
||||
}
|
||||
}
|
||||
|
||||
const templateUsageCheckCache = createCache<string>()
|
||||
|
||||
function resolveTemplateUsageCheckString(sfc: SFCDescriptor) {
|
||||
|
@ -43,27 +43,7 @@ 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 {
|
||||
type: 'style'
|
||||
scoped?: boolean
|
||||
|
Loading…
Reference in New Issue
Block a user