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