refactor: move compile into compiler-core
This commit is contained in:
parent
3e1973f065
commit
8a923f6a52
@ -164,7 +164,7 @@ describe('compiler: transform', () => {
|
|||||||
const ast = parse(`<div/>`)
|
const ast = parse(`<div/>`)
|
||||||
const loc = ast.children[0].loc.start
|
const loc = ast.children[0].loc.start
|
||||||
const plugin: Transform = (node, context) => {
|
const plugin: Transform = (node, context) => {
|
||||||
context.onError(
|
context.emitError(
|
||||||
createCompilerError(ErrorCodes.X_INVALID_END_TAG, node.loc.start)
|
createCompilerError(ErrorCodes.X_INVALID_END_TAG, node.loc.start)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -66,12 +66,11 @@ export function generate(
|
|||||||
|
|
||||||
function createCodegenContext(
|
function createCodegenContext(
|
||||||
ast: RootNode,
|
ast: RootNode,
|
||||||
options: CodegenOptions
|
{ module = false, filename = `template.vue.html` }: CodegenOptions
|
||||||
): CodegenContext {
|
): CodegenContext {
|
||||||
const context: CodegenContext = {
|
const context: CodegenContext = {
|
||||||
module: false,
|
module,
|
||||||
filename: `template.vue.html`,
|
filename,
|
||||||
...options,
|
|
||||||
source: ast.loc.source,
|
source: ast.loc.source,
|
||||||
code: ``,
|
code: ``,
|
||||||
column: 1,
|
column: 1,
|
||||||
|
@ -27,7 +27,7 @@ export const transformFor = createDirectiveTransform(
|
|||||||
children: [node]
|
children: [node]
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
context.onError(
|
context.emitError(
|
||||||
createCompilerError(
|
createCompilerError(
|
||||||
ErrorCodes.X_FOR_MALFORMED_EXPRESSION,
|
ErrorCodes.X_FOR_MALFORMED_EXPRESSION,
|
||||||
dir.loc.start
|
dir.loc.start
|
||||||
@ -35,7 +35,7 @@ export const transformFor = createDirectiveTransform(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
context.onError(
|
context.emitError(
|
||||||
createCompilerError(ErrorCodes.X_FOR_NO_EXPRESSION, dir.loc.start)
|
createCompilerError(ErrorCodes.X_FOR_NO_EXPRESSION, dir.loc.start)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ export const transformIf = createDirectiveTransform(
|
|||||||
}
|
}
|
||||||
sibling.branches.push(branch)
|
sibling.branches.push(branch)
|
||||||
} else {
|
} else {
|
||||||
context.onError(
|
context.emitError(
|
||||||
createCompilerError(
|
createCompilerError(
|
||||||
dir.name === 'else'
|
dir.name === 'else'
|
||||||
? ErrorCodes.X_ELSE_NO_ADJACENT_IF
|
? ErrorCodes.X_ELSE_NO_ADJACENT_IF
|
||||||
|
@ -5,6 +5,10 @@ export interface CompilerError extends SyntaxError {
|
|||||||
loc: Position
|
loc: Position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function defaultOnError(error: CompilerError) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
export function createCompilerError(
|
export function createCompilerError(
|
||||||
code: ErrorCodes,
|
code: ErrorCodes,
|
||||||
loc: Position
|
loc: Position
|
||||||
|
@ -1,11 +1,35 @@
|
|||||||
|
import { parse, ParserOptions } from './parse'
|
||||||
|
import { transform, TransformOptions } from './transform'
|
||||||
|
import { generate, CodegenOptions, CodegenResult } from './codegen'
|
||||||
|
|
||||||
|
export type CompilerOptions = ParserOptions & TransformOptions & CodegenOptions
|
||||||
|
|
||||||
|
export function compile(
|
||||||
|
template: string,
|
||||||
|
options: CompilerOptions = {}
|
||||||
|
): CodegenResult {
|
||||||
|
const ast = parse(template, options)
|
||||||
|
|
||||||
|
transform(ast, {
|
||||||
|
...options,
|
||||||
|
transforms: [
|
||||||
|
// TODO include built-in core transforms
|
||||||
|
...(options.transforms || []) // user transforms
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
return generate(ast, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also expose lower level APIs & types
|
||||||
export { parse, ParserOptions, TextModes } from './parse'
|
export { parse, ParserOptions, TextModes } from './parse'
|
||||||
export {
|
export {
|
||||||
transform,
|
transform,
|
||||||
createDirectiveTransform,
|
createDirectiveTransform,
|
||||||
TransformOptions,
|
TransformOptions,
|
||||||
Transform
|
Transform,
|
||||||
|
DirectiveTransform
|
||||||
} from './transform'
|
} from './transform'
|
||||||
export { generate, CodegenOptions, CodegenResult } from './codegen'
|
export { generate, CodegenOptions, CodegenResult } from './codegen'
|
||||||
export { ErrorCodes, CompilerError, createCompilerError } from './errors'
|
export { ErrorCodes, CompilerError, createCompilerError } from './errors'
|
||||||
|
|
||||||
export * from './ast'
|
export * from './ast'
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
import { ErrorCodes, CompilerError, createCompilerError } from './errors'
|
import {
|
||||||
|
ErrorCodes,
|
||||||
|
CompilerError,
|
||||||
|
createCompilerError,
|
||||||
|
defaultOnError
|
||||||
|
} from './errors'
|
||||||
import {
|
import {
|
||||||
assert,
|
assert,
|
||||||
advancePositionWithMutation,
|
advancePositionWithMutation,
|
||||||
@ -48,9 +53,7 @@ export const defaultParserOptions: Required<ParserOptions> = {
|
|||||||
'apos;': "'",
|
'apos;': "'",
|
||||||
'quot;': '"'
|
'quot;': '"'
|
||||||
},
|
},
|
||||||
onError(error: CompilerError): void {
|
onError: defaultOnError
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum TextModes {
|
export const enum TextModes {
|
||||||
@ -62,7 +65,8 @@ export const enum TextModes {
|
|||||||
ATTRIBUTE_VALUE
|
ATTRIBUTE_VALUE
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ParserContext extends Required<ParserOptions> {
|
interface ParserContext {
|
||||||
|
options: Required<ParserOptions>
|
||||||
readonly originalSource: string
|
readonly originalSource: string
|
||||||
source: string
|
source: string
|
||||||
offset: number
|
offset: number
|
||||||
@ -87,8 +91,10 @@ function createParserContext(
|
|||||||
options: ParserOptions
|
options: ParserOptions
|
||||||
): ParserContext {
|
): ParserContext {
|
||||||
return {
|
return {
|
||||||
|
options: {
|
||||||
...defaultParserOptions,
|
...defaultParserOptions,
|
||||||
...options,
|
...options
|
||||||
|
},
|
||||||
column: 1,
|
column: 1,
|
||||||
line: 1,
|
line: 1,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
@ -115,7 +121,7 @@ function parseChildren(
|
|||||||
const s = context.source
|
const s = context.source
|
||||||
let node: any = null
|
let node: any = null
|
||||||
|
|
||||||
if (startsWith(s, context.delimiters[0])) {
|
if (startsWith(s, context.options.delimiters[0])) {
|
||||||
// '{{'
|
// '{{'
|
||||||
node = parseInterpolation(context, mode)
|
node = parseInterpolation(context, mode)
|
||||||
} else if (mode === TextModes.DATA && s[0] === '<') {
|
} else if (mode === TextModes.DATA && s[0] === '<') {
|
||||||
@ -194,7 +200,11 @@ function pushNode(
|
|||||||
if (!__DEV__ && node.type === NodeTypes.COMMENT) {
|
if (!__DEV__ && node.type === NodeTypes.COMMENT) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (context.ignoreSpaces && node.type === NodeTypes.TEXT && node.isEmpty) {
|
if (
|
||||||
|
context.options.ignoreSpaces &&
|
||||||
|
node.type === NodeTypes.TEXT &&
|
||||||
|
node.isEmpty
|
||||||
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,13 +321,13 @@ function parseElement(
|
|||||||
const parent = last(ancestors)
|
const parent = last(ancestors)
|
||||||
const element = parseTag(context, TagType.Start, parent)
|
const element = parseTag(context, TagType.Start, parent)
|
||||||
|
|
||||||
if (element.isSelfClosing || context.isVoidTag(element.tag)) {
|
if (element.isSelfClosing || context.options.isVoidTag(element.tag)) {
|
||||||
return element
|
return element
|
||||||
}
|
}
|
||||||
|
|
||||||
// Children.
|
// Children.
|
||||||
ancestors.push(element)
|
ancestors.push(element)
|
||||||
const mode = (context.getTextMode(
|
const mode = (context.options.getTextMode(
|
||||||
element.tag,
|
element.tag,
|
||||||
element.ns
|
element.ns
|
||||||
) as unknown) as TextModes
|
) as unknown) as TextModes
|
||||||
@ -368,7 +378,7 @@ function parseTag(
|
|||||||
const tag = match[1]
|
const tag = match[1]
|
||||||
const attrs = []
|
const attrs = []
|
||||||
const directives = []
|
const directives = []
|
||||||
const ns = context.getNamespace(tag, parent)
|
const ns = context.options.getNamespace(tag, parent)
|
||||||
|
|
||||||
advanceBy(context, match[0].length)
|
advanceBy(context, match[0].length)
|
||||||
advanceSpaces(context)
|
advanceSpaces(context)
|
||||||
@ -601,7 +611,7 @@ function parseInterpolation(
|
|||||||
context: ParserContext,
|
context: ParserContext,
|
||||||
mode: TextModes
|
mode: TextModes
|
||||||
): ExpressionNode | undefined {
|
): ExpressionNode | undefined {
|
||||||
const [open, close] = context.delimiters
|
const [open, close] = context.options.delimiters
|
||||||
__DEV__ && assert(startsWith(context.source, open))
|
__DEV__ && assert(startsWith(context.source, open))
|
||||||
|
|
||||||
const closeIndex = context.source.indexOf(close, open.length)
|
const closeIndex = context.source.indexOf(close, open.length)
|
||||||
@ -626,7 +636,7 @@ function parseInterpolation(
|
|||||||
function parseText(context: ParserContext, mode: TextModes): TextNode {
|
function parseText(context: ParserContext, mode: TextModes): TextNode {
|
||||||
__DEV__ && assert(context.source.length > 0)
|
__DEV__ && assert(context.source.length > 0)
|
||||||
|
|
||||||
const [open] = context.delimiters
|
const [open] = context.options.delimiters
|
||||||
const endIndex = Math.min(
|
const endIndex = Math.min(
|
||||||
...[
|
...[
|
||||||
context.source.indexOf('<', 1),
|
context.source.indexOf('<', 1),
|
||||||
@ -691,7 +701,7 @@ function parseTextData(
|
|||||||
--length
|
--length
|
||||||
) {
|
) {
|
||||||
name = context.source.substr(1, length)
|
name = context.source.substr(1, length)
|
||||||
value = context.namedCharacterReferences[name]
|
value = context.options.namedCharacterReferences[name]
|
||||||
}
|
}
|
||||||
if (value) {
|
if (value) {
|
||||||
const semi = name.endsWith(';')
|
const semi = name.endsWith(';')
|
||||||
@ -837,7 +847,7 @@ function emitError(
|
|||||||
loc.offset += offset
|
loc.offset += offset
|
||||||
loc.column += offset
|
loc.column += offset
|
||||||
}
|
}
|
||||||
context.onError(createCompilerError(code, loc))
|
context.options.onError(createCompilerError(code, loc))
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEnd(
|
function isEnd(
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
DirectiveNode
|
DirectiveNode
|
||||||
} from './ast'
|
} from './ast'
|
||||||
import { isString } from '@vue/shared'
|
import { isString } from '@vue/shared'
|
||||||
import { CompilerError } from './errors'
|
import { CompilerError, defaultOnError } from './errors'
|
||||||
|
|
||||||
export type Transform = (node: ChildNode, context: TransformContext) => void
|
export type Transform = (node: ChildNode, context: TransformContext) => void
|
||||||
|
|
||||||
@ -18,11 +18,13 @@ export type DirectiveTransform = (
|
|||||||
) => false | void
|
) => false | void
|
||||||
|
|
||||||
export interface TransformOptions {
|
export interface TransformOptions {
|
||||||
transforms: Transform[]
|
transforms?: Transform[]
|
||||||
onError?: (error: CompilerError) => void
|
onError?: (error: CompilerError) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TransformContext extends Required<TransformOptions> {
|
interface TransformContext {
|
||||||
|
transforms: Transform[]
|
||||||
|
emitError: (error: CompilerError) => void
|
||||||
parent: ParentNode
|
parent: ParentNode
|
||||||
ancestors: ParentNode[]
|
ancestors: ParentNode[]
|
||||||
childIndex: number
|
childIndex: number
|
||||||
@ -42,10 +44,8 @@ function createTransformContext(
|
|||||||
options: TransformOptions
|
options: TransformOptions
|
||||||
): TransformContext {
|
): TransformContext {
|
||||||
const context: TransformContext = {
|
const context: TransformContext = {
|
||||||
onError(error: CompilerError) {
|
transforms: options.transforms || [],
|
||||||
throw error
|
emitError: options.onError || defaultOnError,
|
||||||
},
|
|
||||||
...options,
|
|
||||||
parent: root,
|
parent: root,
|
||||||
ancestors: [],
|
ancestors: [],
|
||||||
childIndex: 0,
|
childIndex: 0,
|
||||||
@ -109,7 +109,7 @@ function traverseNode(
|
|||||||
ancestors: ParentNode[]
|
ancestors: ParentNode[]
|
||||||
) {
|
) {
|
||||||
// apply transform plugins
|
// apply transform plugins
|
||||||
const transforms = context.transforms
|
const { transforms } = context
|
||||||
for (let i = 0; i < transforms.length; i++) {
|
for (let i = 0; i < transforms.length; i++) {
|
||||||
const plugin = transforms[i]
|
const plugin = transforms[i]
|
||||||
plugin(node, context)
|
plugin(node, context)
|
||||||
|
@ -1,46 +1,23 @@
|
|||||||
import {
|
import {
|
||||||
parse,
|
compile as baseCompile,
|
||||||
transform,
|
CompilerOptions,
|
||||||
generate,
|
|
||||||
CompilerError,
|
|
||||||
Transform,
|
|
||||||
CodegenResult
|
CodegenResult
|
||||||
} from '@vue/compiler-core'
|
} from '@vue/compiler-core'
|
||||||
import { parserOptionsMinimal } from './parserOptionsMinimal'
|
import { parserOptionsMinimal } from './parserOptionsMinimal'
|
||||||
import { parserOptionsStandard } from './parserOptionsStandard'
|
import { parserOptionsStandard } from './parserOptionsStandard'
|
||||||
|
|
||||||
const parserOptions = __BROWSER__ ? parserOptionsMinimal : parserOptionsStandard
|
|
||||||
|
|
||||||
export interface CompilerOptions {
|
|
||||||
module?: boolean
|
|
||||||
onError?(err: CompilerError): void
|
|
||||||
transforms?: Transform[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function compile(
|
export function compile(
|
||||||
template: string,
|
template: string,
|
||||||
options: CompilerOptions = {}
|
options: CompilerOptions = {}
|
||||||
): CodegenResult {
|
): CodegenResult {
|
||||||
const {
|
return baseCompile(template, {
|
||||||
module = false,
|
...options,
|
||||||
onError = (err: CompilerError) => {
|
...(__BROWSER__ ? parserOptionsMinimal : parserOptionsStandard),
|
||||||
throw err
|
|
||||||
},
|
|
||||||
transforms = []
|
|
||||||
} = options
|
|
||||||
const ast = parse(template, {
|
|
||||||
...parserOptions,
|
|
||||||
onError
|
|
||||||
})
|
|
||||||
transform(ast, {
|
|
||||||
transforms: [
|
transforms: [
|
||||||
// TODO include core transforms
|
// TODO include DOM-specific transforms
|
||||||
// TODO include DOM transforms
|
...(options.transforms || []) // extra user transforms
|
||||||
...transforms // user transforms
|
]
|
||||||
],
|
|
||||||
onError
|
|
||||||
})
|
})
|
||||||
return generate(ast, { module })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export * from '@vue/compiler-core'
|
export * from '@vue/compiler-core'
|
||||||
|
@ -24,6 +24,7 @@ import {
|
|||||||
isObject
|
isObject
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import { SuspenseBoundary } from './suspense'
|
import { SuspenseBoundary } from './suspense'
|
||||||
|
import { CompilerOptions } from '@vue/compiler-dom'
|
||||||
|
|
||||||
export type Data = { [key: string]: unknown }
|
export type Data = { [key: string]: unknown }
|
||||||
|
|
||||||
@ -61,7 +62,7 @@ export interface SetupContext {
|
|||||||
emit: Emit
|
emit: Emit
|
||||||
}
|
}
|
||||||
|
|
||||||
type RenderFunction = () => VNodeChild
|
export type RenderFunction = () => VNodeChild
|
||||||
|
|
||||||
export interface ComponentInternalInstance {
|
export interface ComponentInternalInstance {
|
||||||
type: FunctionalComponent | ComponentOptions
|
type: FunctionalComponent | ComponentOptions
|
||||||
@ -298,8 +299,14 @@ export function handleSetupResult(
|
|||||||
finishComponentSetup(instance, parentSuspense)
|
finishComponentSetup(instance, parentSuspense)
|
||||||
}
|
}
|
||||||
|
|
||||||
let compile: Function | undefined
|
type CompileFunction = (
|
||||||
export function registerCompiler(_compile: Function) {
|
template: string,
|
||||||
|
options?: CompilerOptions
|
||||||
|
) => RenderFunction
|
||||||
|
|
||||||
|
let compile: CompileFunction | undefined
|
||||||
|
|
||||||
|
export function registerRuntimeCompiler(_compile: CompileFunction) {
|
||||||
compile = _compile
|
compile = _compile
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,7 +318,9 @@ function finishComponentSetup(
|
|||||||
if (!instance.render) {
|
if (!instance.render) {
|
||||||
if (Component.template && !Component.render) {
|
if (Component.template && !Component.render) {
|
||||||
if (compile) {
|
if (compile) {
|
||||||
Component.render = compile(Component.template)
|
Component.render = compile(Component.template, {
|
||||||
|
onError(err) {}
|
||||||
|
})
|
||||||
} else if (__DEV__) {
|
} else if (__DEV__) {
|
||||||
warn(
|
warn(
|
||||||
`Component provides template but the build of Vue you are running ` +
|
`Component provides template but the build of Vue you are running ` +
|
||||||
|
@ -40,7 +40,7 @@ export { applyDirectives } from './directives'
|
|||||||
export { resolveComponent, resolveDirective } from './componentOptions'
|
export { resolveComponent, resolveDirective } from './componentOptions'
|
||||||
|
|
||||||
// Internal, for integration with runtime compiler
|
// Internal, for integration with runtime compiler
|
||||||
export { registerCompiler } from './component'
|
export { registerRuntimeCompiler } from './component'
|
||||||
|
|
||||||
// Types -----------------------------------------------------------------------
|
// Types -----------------------------------------------------------------------
|
||||||
|
|
||||||
@ -50,7 +50,8 @@ export { VNode, VNodeTypes } from './vnode'
|
|||||||
export {
|
export {
|
||||||
Component,
|
Component,
|
||||||
FunctionalComponent,
|
FunctionalComponent,
|
||||||
ComponentInternalInstance
|
ComponentInternalInstance,
|
||||||
|
RenderFunction
|
||||||
} from './component'
|
} from './component'
|
||||||
export {
|
export {
|
||||||
ComponentOptions,
|
ComponentOptions,
|
||||||
@ -58,6 +59,7 @@ export {
|
|||||||
ComponentOptionsWithProps,
|
ComponentOptionsWithProps,
|
||||||
ComponentOptionsWithArrayProps
|
ComponentOptionsWithArrayProps
|
||||||
} from './componentOptions'
|
} from './componentOptions'
|
||||||
|
|
||||||
export { ComponentPublicInstance } from './componentPublicInstanceProxy'
|
export { ComponentPublicInstance } from './componentPublicInstanceProxy'
|
||||||
export { RendererOptions } from './createRenderer'
|
export { RendererOptions } from './createRenderer'
|
||||||
export { Slot, Slots } from './componentSlots'
|
export { Slot, Slots } from './componentSlots'
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
// This package is the "full-build" that includes both the runtime
|
// This package is the "full-build" that includes both the runtime
|
||||||
// and the compiler, and supports on-the-fly compilation of the template option.
|
// and the compiler, and supports on-the-fly compilation of the template option.
|
||||||
import { compile as baseCompile, CompilerOptions } from '@vue/compiler-dom'
|
import { compile, CompilerOptions } from '@vue/compiler-dom'
|
||||||
import { registerCompiler } from '@vue/runtime-dom'
|
import { registerRuntimeCompiler, RenderFunction } from '@vue/runtime-dom'
|
||||||
|
|
||||||
export function compile(template: string, options?: CompilerOptions): Function {
|
function compileToFunction(
|
||||||
const { code } = baseCompile(template, options)
|
template: string,
|
||||||
return new Function(`with(this){return ${code}}`)
|
options?: CompilerOptions
|
||||||
|
): RenderFunction {
|
||||||
|
const { code } = compile(template, options)
|
||||||
|
return new Function(`with(this){return ${code}}`) as RenderFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
registerCompiler(compile)
|
registerRuntimeCompiler(compileToFunction)
|
||||||
|
|
||||||
|
export { compileToFunction as compile }
|
||||||
export * from '@vue/runtime-dom'
|
export * from '@vue/runtime-dom'
|
||||||
|
|
||||||
if (__BROWSER__ && __DEV__) {
|
if (__BROWSER__ && __DEV__) {
|
||||||
|
Loading…
Reference in New Issue
Block a user