refactor(compiler-sfc): remove parseOnly mode

This commit is contained in:
Evan You 2021-08-22 22:28:47 -04:00
parent db8dc753c0
commit 0c2ea1c134
3 changed files with 16 additions and 217 deletions

View File

@ -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()
})
})

View File

@ -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) {

View File

@ -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