import { parse } from '../../src/parse'
import { transform } from '../../src/transform'
-import { transformFor } from '../../src/directives/vFor'
+import { transformFor } from '../../src/transforms/vFor'
import { ForNode, NodeTypes } from '../../src/ast'
import { ErrorCodes } from '../../src/errors'
test('number expression', () => {
const node = parse('<span v-for="index in 5" />')
- transform(node, { transforms: [transformFor] })
+ transform(node, { nodeTransforms: [transformFor] })
expect(node.children.length).toBe(1)
test('value', () => {
const node = parse('<span v-for="(item) in items" />')
- transform(node, { transforms: [transformFor] })
+ transform(node, { nodeTransforms: [transformFor] })
expect(node.children.length).toBe(1)
test('object de-structured value', () => {
const node = parse('<span v-for="({ id, value }) in items" />')
- transform(node, { transforms: [transformFor] })
+ transform(node, { nodeTransforms: [transformFor] })
expect(node.children.length).toBe(1)
test('array de-structured value', () => {
const node = parse('<span v-for="([ id, value ]) in items" />')
- transform(node, { transforms: [transformFor] })
+ transform(node, { nodeTransforms: [transformFor] })
expect(node.children.length).toBe(1)
test('value and key', () => {
const node = parse('<span v-for="(item, key) in items" />')
- transform(node, { transforms: [transformFor] })
+ transform(node, { nodeTransforms: [transformFor] })
expect(node.children.length).toBe(1)
test('value, key and index', () => {
const node = parse('<span v-for="(value, key, index) in items" />')
- transform(node, { transforms: [transformFor] })
+ transform(node, { nodeTransforms: [transformFor] })
expect(node.children.length).toBe(1)
test('skipped key', () => {
const node = parse('<span v-for="(value,,index) in items" />')
- transform(node, { transforms: [transformFor] })
+ transform(node, { nodeTransforms: [transformFor] })
expect(node.children.length).toBe(1)
test('skipped value and key', () => {
const node = parse('<span v-for="(,,index) in items" />')
- transform(node, { transforms: [transformFor] })
+ transform(node, { nodeTransforms: [transformFor] })
expect(node.children.length).toBe(1)
test('unbracketed value', () => {
const node = parse('<span v-for="item in items" />')
- transform(node, { transforms: [transformFor] })
+ transform(node, { nodeTransforms: [transformFor] })
expect(node.children.length).toBe(1)
test('unbracketed value and key', () => {
const node = parse('<span v-for="item, key in items" />')
- transform(node, { transforms: [transformFor] })
+ transform(node, { nodeTransforms: [transformFor] })
expect(node.children.length).toBe(1)
test('unbracketed value, key and index', () => {
const node = parse('<span v-for="value, key, index in items" />')
- transform(node, { transforms: [transformFor] })
+ transform(node, { nodeTransforms: [transformFor] })
expect(node.children.length).toBe(1)
test('unbracketed skipped key', () => {
const node = parse('<span v-for="value, , index in items" />')
- transform(node, { transforms: [transformFor] })
+ transform(node, { nodeTransforms: [transformFor] })
expect(node.children.length).toBe(1)
test('unbracketed skipped value and key', () => {
const node = parse('<span v-for=", , index in items" />')
- transform(node, { transforms: [transformFor] })
+ transform(node, { nodeTransforms: [transformFor] })
expect(node.children.length).toBe(1)
test('missing expression', () => {
const node = parse('<span v-for />')
const onError = jest.fn()
- transform(node, { transforms: [transformFor], onError })
+ transform(node, { nodeTransforms: [transformFor], onError })
expect(onError).toHaveBeenCalledTimes(1)
expect(onError).toHaveBeenCalledWith(
test('empty expression', () => {
const node = parse('<span v-for="" />')
const onError = jest.fn()
- transform(node, { transforms: [transformFor], onError })
+ transform(node, { nodeTransforms: [transformFor], onError })
expect(onError).toHaveBeenCalledTimes(1)
expect(onError).toHaveBeenCalledWith(
test('invalid expression', () => {
const node = parse('<span v-for="items" />')
const onError = jest.fn()
- transform(node, { transforms: [transformFor], onError })
+ transform(node, { nodeTransforms: [transformFor], onError })
expect(onError).toHaveBeenCalledTimes(1)
expect(onError).toHaveBeenCalledWith(
test('missing source', () => {
const node = parse('<span v-for="item in" />')
const onError = jest.fn()
- transform(node, { transforms: [transformFor], onError })
+ transform(node, { nodeTransforms: [transformFor], onError })
expect(onError).toHaveBeenCalledTimes(1)
expect(onError).toHaveBeenCalledWith(
test('missing value', () => {
const node = parse('<span v-for="in items" />')
const onError = jest.fn()
- transform(node, { transforms: [transformFor], onError })
+ transform(node, { nodeTransforms: [transformFor], onError })
expect(onError).toHaveBeenCalledTimes(1)
expect(onError).toHaveBeenCalledWith(
const source = '<span v-for="item in items" />'
const node = parse(source)
- transform(node, { transforms: [transformFor] })
+ transform(node, { nodeTransforms: [transformFor] })
expect(node.children.length).toBe(1)
const source = '<span v-for="( item ) in items" />'
const node = parse(source)
- transform(node, { transforms: [transformFor] })
+ transform(node, { nodeTransforms: [transformFor] })
expect(node.children.length).toBe(1)
const source = '<span v-for="( { id, key })in items" />'
const node = parse(source)
- transform(node, { transforms: [transformFor] })
+ transform(node, { nodeTransforms: [transformFor] })
expect(node.children.length).toBe(1)
const source = '<span v-for="( item, key, index ) in items" />'
const node = parse(source)
- transform(node, { transforms: [transformFor] })
+ transform(node, { nodeTransforms: [transformFor] })
expect(node.children.length).toBe(1)
const source = '<span v-for="( item,, index ) in items" />'
const node = parse(source)
- transform(node, { transforms: [transformFor] })
+ transform(node, { nodeTransforms: [transformFor] })
expect(node.children.length).toBe(1)
import { parse } from '../../src/parse'
import { transform } from '../../src/transform'
-import { transformIf } from '../../src/directives/vIf'
+import { transformIf } from '../../src/transforms/vIf'
import {
IfNode,
NodeTypes,
test('basic v-if', () => {
const ast = parse(`<div v-if="ok"/>`)
transform(ast, {
- transforms: [transformIf]
+ nodeTransforms: [transformIf]
})
const node = ast.children[0] as IfNode
expect(node.type).toBe(NodeTypes.IF)
test('template v-if', () => {
const ast = parse(`<template v-if="ok"><div/>hello<p/></template>`)
transform(ast, {
- transforms: [transformIf]
+ nodeTransforms: [transformIf]
})
const node = ast.children[0] as IfNode
expect(node.type).toBe(NodeTypes.IF)
test('v-if + v-else', () => {
const ast = parse(`<div v-if="ok"/><p v-else/>`)
transform(ast, {
- transforms: [transformIf]
+ nodeTransforms: [transformIf]
})
// should fold branches
expect(ast.children.length).toBe(1)
test('v-if + v-else-if', () => {
const ast = parse(`<div v-if="ok"/><p v-else-if="orNot"/>`)
transform(ast, {
- transforms: [transformIf]
+ nodeTransforms: [transformIf]
})
// should fold branches
expect(ast.children.length).toBe(1)
`<div v-if="ok"/><p v-else-if="orNot"/><template v-else>fine</template>`
)
transform(ast, {
- transforms: [transformIf]
+ nodeTransforms: [transformIf]
})
// should fold branches
expect(ast.children.length).toBe(1)
<template v-else>fine</template>
`)
transform(ast, {
- transforms: [transformIf]
+ nodeTransforms: [transformIf]
})
// should fold branches
expect(ast.children.length).toBe(1)
const ast = parse(`<div v-else/>`)
const spy = jest.fn()
transform(ast, {
- transforms: [transformIf],
+ nodeTransforms: [transformIf],
onError: spy
})
expect(spy.mock.calls[0]).toMatchObject([
const ast2 = parse(`<div/><div v-else/>`)
const spy2 = jest.fn()
transform(ast2, {
- transforms: [transformIf],
+ nodeTransforms: [transformIf],
onError: spy2
})
expect(spy2.mock.calls[0]).toMatchObject([
const ast3 = parse(`<div/>foo<div v-else/>`)
const spy3 = jest.fn()
transform(ast3, {
- transforms: [transformIf],
+ nodeTransforms: [transformIf],
onError: spy3
})
expect(spy3.mock.calls[0]).toMatchObject([
const ast = parse(`<div v-else-if="foo"/>`)
const spy = jest.fn()
transform(ast, {
- transforms: [transformIf],
+ nodeTransforms: [transformIf],
onError: spy
})
expect(spy.mock.calls[0]).toMatchObject([
const ast2 = parse(`<div/><div v-else-if="foo"/>`)
const spy2 = jest.fn()
transform(ast2, {
- transforms: [transformIf],
+ nodeTransforms: [transformIf],
onError: spy2
})
expect(spy2.mock.calls[0]).toMatchObject([
const ast3 = parse(`<div/>foo<div v-else-if="foo"/>`)
const spy3 = jest.fn()
transform(ast3, {
- transforms: [transformIf],
+ nodeTransforms: [transformIf],
onError: spy3
})
expect(spy3.mock.calls[0]).toMatchObject([
import { parse } from '../src/parse'
-import { transform, Transform } from '../src/transform'
+import { transform, NodeTransform } from '../src/transform'
import { ElementNode, NodeTypes } from '../src/ast'
import { ErrorCodes, createCompilerError } from '../src/errors'
// manually store call arguments because context is mutable and shared
// across calls
const calls: any[] = []
- const plugin: Transform = (node, context) => {
+ const plugin: NodeTransform = (node, context) => {
calls.push([node, Object.assign({}, context)])
}
transform(ast, {
- transforms: [plugin]
+ nodeTransforms: [plugin]
})
const div = ast.children[0] as ElementNode
test('context.replaceNode', () => {
const ast = parse(`<div/><span/>`)
- const plugin: Transform = (node, context) => {
+ const plugin: NodeTransform = (node, context) => {
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
// change the node to <p>
context.replaceNode(
}
const spy = jest.fn(plugin)
transform(ast, {
- transforms: [spy]
+ nodeTransforms: [spy]
})
expect(ast.children.length).toBe(2)
const c1 = ast.children[0]
const c2 = ast.children[2]
- const plugin: Transform = (node, context) => {
+ const plugin: NodeTransform = (node, context) => {
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
context.removeNode()
}
}
const spy = jest.fn(plugin)
transform(ast, {
- transforms: [spy]
+ nodeTransforms: [spy]
})
expect(ast.children.length).toBe(2)
const c1 = ast.children[0]
const c2 = ast.children[2]
- const plugin: Transform = (node, context) => {
+ const plugin: NodeTransform = (node, context) => {
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
context.removeNode()
// remove previous sibling
}
const spy = jest.fn(plugin)
transform(ast, {
- transforms: [spy]
+ nodeTransforms: [spy]
})
expect(ast.children.length).toBe(1)
const c1 = ast.children[0]
const d1 = ast.children[1]
- const plugin: Transform = (node, context) => {
+ const plugin: NodeTransform = (node, context) => {
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
context.removeNode()
// remove next sibling
}
const spy = jest.fn(plugin)
transform(ast, {
- transforms: [spy]
+ nodeTransforms: [spy]
})
expect(ast.children.length).toBe(1)
test('onError option', () => {
const ast = parse(`<div/>`)
const loc = ast.children[0].loc.start
- const plugin: Transform = (node, context) => {
- context.emitError(
+ const plugin: NodeTransform = (node, context) => {
+ context.onError(
createCompilerError(ErrorCodes.X_INVALID_END_TAG, node.loc.start)
)
}
const spy = jest.fn()
transform(ast, {
- transforms: [plugin],
+ nodeTransforms: [plugin],
onError: spy
})
expect(spy.mock.calls[0]).toMatchObject([
tag: string
tagType: ElementTypes
isSelfClosing: boolean
- attrs: AttributeNode[]
- directives: DirectiveNode[]
+ props: Array<AttributeNode | DirectiveNode>
children: ChildNode[]
codegenNode: CallExpression | undefined
}
type: NodeTypes.ARRAY_EXPRESSION
elements: Array<CodegenNode>
}
+
+export function createArrayExpression(
+ elements: ArrayExpression['elements'],
+ loc: SourceLocation
+): ArrayExpression {
+ return {
+ type: NodeTypes.ARRAY_EXPRESSION,
+ loc,
+ elements
+ }
+}
+
+export function createObjectExpression(
+ properties: Property[],
+ loc: SourceLocation
+): ObjectExpression {
+ return {
+ type: NodeTypes.OBJECT_EXPRESSION,
+ loc,
+ properties
+ }
+}
+
+export function createObjectProperty(
+ key: ExpressionNode,
+ value: ExpressionNode,
+ loc: SourceLocation
+): Property {
+ return {
+ type: NodeTypes.PROPERTY,
+ loc,
+ key,
+ value
+ }
+}
+
+export function createExpression(
+ content: string,
+ isStatic: boolean,
+ loc: SourceLocation
+): ExpressionNode {
+ return {
+ type: NodeTypes.EXPRESSION,
+ loc,
+ content,
+ isStatic
+ }
+}
+
+export function createCallExpression(
+ callee: string,
+ args: CallExpression['arguments'],
+ loc: SourceLocation
+): CallExpression {
+ return {
+ type: NodeTypes.CALL_EXPRESSION,
+ loc,
+ callee,
+ arguments: args
+ }
+}
X_ELSE_IF_NO_ADJACENT_IF,
X_ELSE_NO_ADJACENT_IF,
X_FOR_NO_EXPRESSION,
- X_FOR_MALFORMED_EXPRESSION
+ X_FOR_MALFORMED_EXPRESSION,
+ X_V_BIND_NO_EXPRESSION
}
export const errorMessages: { [code: number]: string } = {
transform(ast, {
...options,
- transforms: [
+ nodeTransforms: [
// TODO include built-in core transforms
- ...(options.transforms || []) // user transforms
- ]
+ ...(options.nodeTransforms || []) // user transforms
+ ],
+ directiveTransforms: {
+ // TODO include built-in directive transforms
+ ...(options.directiveTransforms || {}) // user transforms
+ }
})
return generate(ast, options)
export { parse, ParserOptions, TextModes } from './parse'
export {
transform,
- createDirectiveTransform,
+ createStructuralDirectiveTransform,
TransformOptions,
TransformContext,
- Transform,
- DirectiveTransform
+ NodeTransform as Transform,
+ StructuralDirectiveTransform
} from './transform'
export {
generate,
} from './codegen'
export { ErrorCodes, CompilerError, createCompilerError } from './errors'
export * from './ast'
+
+// debug
+export { prepareElementForCodegen } from './transforms/element'
const start = getCursor(context)
const match = /^<\/?([a-z][^\t\r\n\f />]*)/i.exec(context.source)!
const tag = match[1]
- const attrs = []
- const directives = []
+ const props = []
const ns = context.options.getNamespace(tag, parent)
advanceBy(context, match[0].length)
const attr = parseAttribute(context, attributeNames)
if (type === TagType.Start) {
- if (attr.type === NodeTypes.DIRECTIVE) {
- directives.push(attr)
- } else {
- attrs.push(attr)
- }
+ props.push(attr)
}
if (/^[^\t\r\n\f />]/.test(context.source)) {
ns,
tag,
tagType,
- attrs,
- directives,
+ props,
isSelfClosing,
children: [],
- loc: getSelection(context, start)
+ loc: getSelection(context, start),
+ codegenNode: undefined // to be created during transform phase
}
}
ParentNode,
ChildNode,
ElementNode,
- DirectiveNode
+ DirectiveNode,
+ Property
} from './ast'
import { isString } from '@vue/shared'
import { CompilerError, defaultOnError } from './errors'
-export type Transform = (node: ChildNode, context: TransformContext) => void
+// There are two types of transforms:
+//
+// - NodeTransform:
+// Transforms that operate directly on a ChildNode. NodeTransforms may mutate,
+// replace or remove the node being processed.
+export type NodeTransform = (node: ChildNode, context: TransformContext) => void
+// - DirectiveTransform:
+// Transforms that handles a single directive attribute on an element.
+// It translates the raw directive into actual props for the VNode.
export type DirectiveTransform = (
+ dir: DirectiveNode,
+ context: TransformContext
+) => {
+ props: Property | Property[]
+ needRuntime: boolean
+}
+
+// A structural directive transform is a techically a NodeTransform;
+// Only v-if and v-for fall into this category.
+export type StructuralDirectiveTransform = (
node: ElementNode,
dir: DirectiveNode,
context: TransformContext
-) => false | void
+) => void
export interface TransformOptions {
- transforms?: Transform[]
+ nodeTransforms?: NodeTransform[]
+ directiveTransforms?: { [name: string]: DirectiveTransform }
onError?: (error: CompilerError) => void
}
-export interface TransformContext {
- transforms: Transform[]
- emitError: (error: CompilerError) => void
+export interface TransformContext extends Required<TransformOptions> {
parent: ParentNode
ancestors: ParentNode[]
childIndex: number
options: TransformOptions
): TransformContext {
const context: TransformContext = {
- transforms: options.transforms || [],
- emitError: options.onError || defaultOnError,
+ nodeTransforms: options.nodeTransforms || [],
+ directiveTransforms: options.directiveTransforms || {},
+ onError: options.onError || defaultOnError,
parent: root,
ancestors: [],
childIndex: 0,
ancestors: ParentNode[]
) {
// apply transform plugins
- const { transforms } = context
- for (let i = 0; i < transforms.length; i++) {
- const plugin = transforms[i]
+ const { nodeTransforms } = context
+ for (let i = 0; i < nodeTransforms.length; i++) {
+ const plugin = nodeTransforms[i]
plugin(node, context)
if (!context.currentNode) {
return
}
}
-const identity = <T>(_: T): T => _
-
-export function createDirectiveTransform(
+export function createStructuralDirectiveTransform(
name: string | RegExp,
- fn: DirectiveTransform
-): Transform {
+ fn: StructuralDirectiveTransform
+): NodeTransform {
const matches = isString(name)
? (n: string) => n === name
: (n: string) => name.test(n)
return (node, context) => {
if (node.type === NodeTypes.ELEMENT) {
- const dirs = node.directives
- let didRemove = false
- for (let i = 0; i < dirs.length; i++) {
- if (matches(dirs[i].name)) {
- const res = fn(node, dirs[i], context)
- // Directives are removed after transformation by default. A transform
- // returning false means the directive should not be removed.
- if (res !== false) {
- ;(dirs as any)[i] = undefined
- didRemove = true
- }
+ const { props } = node
+ for (let i = 0; i < props.length; i++) {
+ const prop = props[i]
+ if (prop.type === NodeTypes.DIRECTIVE && matches(prop.name)) {
+ fn(node, prop, context)
+ // structural directives are removed after being processed
+ // to avoid infinite recursion
+ props.splice(i, 1)
+ i--
}
}
- if (didRemove) {
- node.directives = dirs.filter(identity)
- }
}
}
}
-import { Transform, TransformContext } from '../transform'
+import { NodeTransform, TransformContext } from '../transform'
import {
NodeTypes,
ElementTypes,
CallExpression,
ObjectExpression,
- ElementNode
+ ElementNode,
+ DirectiveNode,
+ ExpressionNode,
+ ArrayExpression,
+ createCallExpression,
+ createArrayExpression,
+ createObjectProperty,
+ createExpression,
+ createObjectExpression
} from '../ast'
+import { isArray } from '@vue/shared'
+import { createCompilerError, ErrorCodes } from '../errors'
// generate a JavaScript AST for this element's codegen
-export const prepareElementForCodegen: Transform = (node, context) => {
+export const prepareElementForCodegen: NodeTransform = (node, context) => {
if (node.type === NodeTypes.ELEMENT) {
if (
node.tagType === ElementTypes.ELEMENT ||
node.tagType === ElementTypes.COMPONENT
) {
const isComponent = node.tagType === ElementTypes.ELEMENT
- const hasProps = node.attrs.length > 0 || node.directives.length > 0
+ const hasProps = node.props.length > 0
const hasChildren = node.children.length > 0
+ let runtimeDirectives: DirectiveNode[] | undefined
const args: CallExpression['arguments'] = [
+ // TODO inject resolveComponent dep to root
isComponent ? node.tag : `"${node.tag}"`
]
// props
if (hasProps) {
- args.push(buildProps(node))
+ const { props, directives } = buildProps(node, context)
+ args.push(props)
+ runtimeDirectives = directives
}
// children
if (hasChildren) {
args.push(isComponent ? buildSlots(node, context) : node.children)
}
- node.codegenNode = {
- type: NodeTypes.CALL_EXPRESSION,
- loc: node.loc,
- callee: `h`,
- arguments: args
+ const { loc } = node
+ const vnode = createCallExpression(`h`, args, loc)
+
+ if (runtimeDirectives) {
+ node.codegenNode = createCallExpression(
+ `applyDirectives`,
+ [
+ vnode,
+ createArrayExpression(
+ runtimeDirectives.map(dir => {
+ return createDirectiveArgs(dir, context)
+ }),
+ loc
+ )
+ ],
+ loc
+ )
+ } else {
+ node.codegenNode = vnode
}
+ } else if (node.tagType === ElementTypes.SLOT) {
+ // <slot [name="xxx"]/>
+ // TODO
+ } else if (node.tagType === ElementTypes.TEMPLATE) {
+ // do nothing
}
}
}
-function buildProps({ loc, attrs }: ElementNode): ObjectExpression {
- return {
- type: NodeTypes.OBJECT_EXPRESSION,
- loc,
- // At this stage we will only process static attrs. Directive bindings will
- // be handled by their respective transforms which adds/modifies the props.
- properties: attrs.map(({ name, value, loc }) => {
- return {
- type: NodeTypes.PROPERTY,
- loc,
- key: {
- type: NodeTypes.EXPRESSION,
- loc,
- content: name,
- isStatic: true
- },
- value: {
- type: NodeTypes.EXPRESSION,
- loc: value ? value.loc : loc,
- content: value ? value.content : '',
- isStatic: true
+function buildProps(
+ { loc, props }: ElementNode,
+ context: TransformContext
+): {
+ props: ObjectExpression | CallExpression
+ directives: DirectiveNode[]
+} {
+ let properties: ObjectExpression['properties'] = []
+ const mergeArgs: Array<ObjectExpression | ExpressionNode> = []
+ const runtimeDirectives: DirectiveNode[] = []
+
+ for (let i = 0; i < props.length; i++) {
+ // static attribute
+ const prop = props[i]
+ if (prop.type === NodeTypes.ATTRIBUTE) {
+ const { loc, name, value } = prop
+ properties.push(
+ createObjectProperty(
+ createExpression(name, true, loc),
+ createExpression(
+ value ? value.content : '',
+ true,
+ value ? value.loc : loc
+ ),
+ loc
+ )
+ )
+ } else {
+ // directives
+ // special case for v-bind with no argument
+ if (prop.name === 'bind' && !prop.arg) {
+ if (prop.exp) {
+ if (properties.length) {
+ mergeArgs.push(createObjectExpression(properties, loc))
+ properties = []
+ }
+ mergeArgs.push(prop.exp)
+ } else {
+ context.onError(
+ createCompilerError(
+ ErrorCodes.X_V_BIND_NO_EXPRESSION,
+ prop.loc.start
+ )
+ )
+ }
+ continue
+ }
+
+ const directiveTransform = context.directiveTransforms[prop.name]
+ if (directiveTransform) {
+ const { props, needRuntime } = directiveTransform(prop, context)
+ if (isArray(props)) {
+ properties.push(...props)
+ } else {
+ properties.push(props)
+ }
+ if (needRuntime) {
+ runtimeDirectives.push(prop)
}
+ } else {
+ // no built-in transform, this is a user custom directive.
+ runtimeDirectives.push(prop)
}
- })
+ }
+ }
+
+ let ret: ObjectExpression | CallExpression
+
+ // has v-bind="object", wrap with mergeProps
+ if (mergeArgs.length) {
+ if (properties.length) {
+ mergeArgs.push(createObjectExpression(properties, loc))
+ }
+ if (mergeArgs.length > 1) {
+ ret = createCallExpression(`mergeProps`, mergeArgs, loc)
+ } else {
+ // single v-bind with nothing else - no need for a mergeProps call
+ ret = createObjectExpression(properties, loc)
+ }
+ } else {
+ ret = createObjectExpression(properties, loc)
+ }
+
+ return {
+ props: ret,
+ directives: runtimeDirectives
}
}
+function createDirectiveArgs(
+ dir: DirectiveNode,
+ context: TransformContext
+): ArrayExpression {
+ // TODO inject resolveDirective dep to root
+ const dirArgs: ArrayExpression['elements'] = [dir.name]
+ const { loc } = dir
+ if (dir.exp) dirArgs.push(dir.exp)
+ if (dir.arg) dirArgs.push(dir.arg)
+ if (Object.keys(dir.modifiers).length) {
+ dirArgs.push(
+ createObjectExpression(
+ dir.modifiers.map(modifier =>
+ createObjectProperty(
+ createExpression(modifier, true, loc),
+ createExpression(`true`, false, loc),
+ loc
+ )
+ ),
+ loc
+ )
+ )
+ }
+ return createArrayExpression(dirArgs, dir.loc)
+}
+
function buildSlots(
{ loc, children }: ElementNode,
context: TransformContext
): ObjectExpression {
- const slots: ObjectExpression = {
- type: NodeTypes.OBJECT_EXPRESSION,
- loc,
- properties: []
- }
-
+ const slots = createObjectExpression([], loc)
// TODO
return slots
-import { createDirectiveTransform } from '../transform'
-import { NodeTypes, ExpressionNode } from '../ast'
+import { createStructuralDirectiveTransform } from '../transform'
+import { NodeTypes, ExpressionNode, createExpression } from '../ast'
import { createCompilerError, ErrorCodes } from '../errors'
import { getInnerRange } from '../utils'
const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
const stripParensRE = /^\(|\)$/g
-export const transformFor = createDirectiveTransform(
+export const transformFor = createStructuralDirectiveTransform(
'for',
(node, dir, context) => {
if (dir.exp) {
children: [node]
})
} else {
- context.emitError(
+ context.onError(
createCompilerError(
ErrorCodes.X_FOR_MALFORMED_EXPRESSION,
dir.loc.start
)
}
} else {
- context.emitError(
+ context.onError(
createCompilerError(ErrorCodes.X_FOR_NO_EXPRESSION, dir.loc.start)
)
}
node: ExpressionNode
): ExpressionNode | undefined {
if (alias) {
- return {
- type: NodeTypes.EXPRESSION,
- loc: getInnerRange(node.loc, alias.offset, alias.content.length),
- content: alias.content,
- isStatic: false
- }
+ return createExpression(
+ alias.content,
+ false,
+ getInnerRange(node.loc, alias.offset, alias.content.length)
+ )
}
}
-import { createDirectiveTransform } from '../transform'
+import { createStructuralDirectiveTransform } from '../transform'
import {
NodeTypes,
ElementTypes,
} from '../ast'
import { createCompilerError, ErrorCodes } from '../errors'
-export const transformIf = createDirectiveTransform(
+export const transformIf = createStructuralDirectiveTransform(
/^(if|else|else-if)$/,
(node, dir, context) => {
if (dir.name === 'if') {
}
sibling.branches.push(branch)
} else {
- context.emitError(
+ context.onError(
createCompilerError(
dir.name === 'else'
? ErrorCodes.X_ELSE_NO_ADJACENT_IF
import { parserOptionsMinimal } from './parserOptionsMinimal'
import { parserOptionsStandard } from './parserOptionsStandard'
+export * from '@vue/compiler-core'
+
export function compile(
template: string,
options: CompilerOptions = {}
return baseCompile(template, {
...options,
...(__BROWSER__ ? parserOptionsMinimal : parserOptionsStandard),
- transforms: [
- // TODO include DOM-specific transforms
- ...(options.transforms || []) // extra user transforms
- ]
+ directiveTransforms: {
+ // TODO include DOM-specific directiveTransforms
+ ...(options.directiveTransforms || {})
+ }
})
}
-
-export * from '@vue/compiler-core'