test: tests for codegen
This commit is contained in:
parent
76a1196935
commit
7a46e51815
@ -0,0 +1,109 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`compiler: codegen callExpression + objectExpression + arrayExpression 1`] = `
|
||||||
|
"return function render() {
|
||||||
|
with (this) {
|
||||||
|
return createVNode(\\"div\\", {
|
||||||
|
id: \\"foo\\",
|
||||||
|
[prop]: bar
|
||||||
|
}, [
|
||||||
|
foo,
|
||||||
|
createVNode(\\"p\\")
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: codegen comment 1`] = `
|
||||||
|
"return function render() {
|
||||||
|
with (this) {
|
||||||
|
return createVNode(Comment, 0, \\"foo\\")
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: codegen forNode 1`] = `
|
||||||
|
"return function render() {
|
||||||
|
with (this) {
|
||||||
|
return renderList(list, (v, k, i) => toString(v))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: codegen function mode preamble 1`] = `
|
||||||
|
"const { helperOne, helperTwo } = Vue
|
||||||
|
|
||||||
|
return function render() {
|
||||||
|
with (this) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: codegen ifNode 1`] = `
|
||||||
|
"return function render() {
|
||||||
|
with (this) {
|
||||||
|
return (foo)
|
||||||
|
? \\"foo\\"
|
||||||
|
: (bar)
|
||||||
|
? toString(bye)
|
||||||
|
: createVNode(Comment, 0, \\"foo\\")
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: codegen interpolation 1`] = `
|
||||||
|
"return function render() {
|
||||||
|
with (this) {
|
||||||
|
return toString(hello)
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: codegen module mode preamble 1`] = `
|
||||||
|
"import { helperOne, helperTwo } from 'vue'
|
||||||
|
|
||||||
|
export default function render() {
|
||||||
|
with (this) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: codegen prefixIdentifiers: true should inject _ctx statement 1`] = `
|
||||||
|
"return function render() {
|
||||||
|
const _ctx = this
|
||||||
|
return null
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: codegen statement preambles 1`] = `
|
||||||
|
"return function render() {
|
||||||
|
const a = 1
|
||||||
|
const b = 2
|
||||||
|
|
||||||
|
with (this) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: codegen static text 1`] = `
|
||||||
|
"return function render() {
|
||||||
|
with (this) {
|
||||||
|
return \\"hello\\"
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: codegen text + comment + interpolation 1`] = `
|
||||||
|
"return function render() {
|
||||||
|
with (this) {
|
||||||
|
return [
|
||||||
|
\\"foo\\",
|
||||||
|
toString(hello),
|
||||||
|
createVNode(Comment, 0, \\"foo\\")
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
@ -1,14 +1,298 @@
|
|||||||
import { parse, generate } from '../src'
|
import {
|
||||||
|
parse,
|
||||||
|
generate,
|
||||||
|
NodeTypes,
|
||||||
|
RootNode,
|
||||||
|
SourceLocation,
|
||||||
|
createExpression,
|
||||||
|
Namespaces,
|
||||||
|
ElementTypes,
|
||||||
|
createObjectExpression,
|
||||||
|
createObjectProperty,
|
||||||
|
createArrayExpression
|
||||||
|
} from '../src'
|
||||||
import { SourceMapConsumer, RawSourceMap } from 'source-map'
|
import { SourceMapConsumer, RawSourceMap } from 'source-map'
|
||||||
|
import { CREATE_VNODE, COMMENT, TO_STRING } from '../src/runtimeConstants'
|
||||||
|
|
||||||
|
const mockLoc: SourceLocation = {
|
||||||
|
source: ``,
|
||||||
|
start: {
|
||||||
|
offset: 0,
|
||||||
|
line: 1,
|
||||||
|
column: 1
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
offset: 3,
|
||||||
|
line: 1,
|
||||||
|
column: 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRoot(options: Partial<RootNode> = {}): RootNode {
|
||||||
|
return {
|
||||||
|
type: NodeTypes.ROOT,
|
||||||
|
children: [],
|
||||||
|
imports: [],
|
||||||
|
statements: [],
|
||||||
|
loc: mockLoc,
|
||||||
|
...options
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
describe('compiler: codegen', () => {
|
describe('compiler: codegen', () => {
|
||||||
|
test('module mode preamble', () => {
|
||||||
|
const root = createRoot({
|
||||||
|
imports: [`helperOne`, `helperTwo`]
|
||||||
|
})
|
||||||
|
const { code } = generate(root, { mode: 'module' })
|
||||||
|
expect(code).toMatch(`import { helperOne, helperTwo } from 'vue'`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('function mode preamble', () => {
|
||||||
|
const root = createRoot({
|
||||||
|
imports: [`helperOne`, `helperTwo`]
|
||||||
|
})
|
||||||
|
const { code } = generate(root, { mode: 'function' })
|
||||||
|
expect(code).toMatch(`const { helperOne, helperTwo } = Vue`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('statement preambles', () => {
|
||||||
|
const root = createRoot({
|
||||||
|
statements: [`const a = 1`, `const b = 2`]
|
||||||
|
})
|
||||||
|
const { code } = generate(root, { mode: 'function' })
|
||||||
|
expect(code).toMatch(`const a = 1\n`)
|
||||||
|
expect(code).toMatch(`const b = 2\n`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('prefixIdentifiers: true should inject _ctx statement', () => {
|
||||||
|
const { code } = generate(createRoot(), { prefixIdentifiers: true })
|
||||||
|
expect(code).toMatch(`const _ctx = this\n`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('static text', () => {
|
||||||
|
const { code } = generate(
|
||||||
|
createRoot({
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: NodeTypes.TEXT,
|
||||||
|
content: 'hello',
|
||||||
|
isEmpty: false,
|
||||||
|
loc: mockLoc
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(code).toMatch(`return "hello"`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('interpolation', () => {
|
||||||
|
const { code } = generate(
|
||||||
|
createRoot({
|
||||||
|
children: [createExpression(`hello`, false, mockLoc, true)]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(code).toMatch(`return toString(hello)`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('comment', () => {
|
||||||
|
const { code } = generate(
|
||||||
|
createRoot({
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: NodeTypes.COMMENT,
|
||||||
|
content: 'foo',
|
||||||
|
loc: mockLoc
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(code).toMatch(`return ${CREATE_VNODE}(${COMMENT}, 0, "foo")`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('text + comment + interpolation', () => {
|
||||||
|
const { code } = generate(
|
||||||
|
createRoot({
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: NodeTypes.TEXT,
|
||||||
|
content: 'foo',
|
||||||
|
isEmpty: false,
|
||||||
|
loc: mockLoc
|
||||||
|
},
|
||||||
|
createExpression(`hello`, false, mockLoc, true),
|
||||||
|
{
|
||||||
|
type: NodeTypes.COMMENT,
|
||||||
|
content: 'foo',
|
||||||
|
loc: mockLoc
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(code).toMatch(`
|
||||||
|
return [
|
||||||
|
"foo",
|
||||||
|
toString(hello),
|
||||||
|
${CREATE_VNODE}(${COMMENT}, 0, "foo")
|
||||||
|
]`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ifNode', () => {
|
||||||
|
const { code } = generate(
|
||||||
|
createRoot({
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: NodeTypes.IF,
|
||||||
|
loc: mockLoc,
|
||||||
|
isRoot: true,
|
||||||
|
branches: [
|
||||||
|
{
|
||||||
|
type: NodeTypes.IF_BRANCH,
|
||||||
|
condition: createExpression('foo', false, mockLoc),
|
||||||
|
loc: mockLoc,
|
||||||
|
isRoot: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: NodeTypes.TEXT,
|
||||||
|
content: 'foo',
|
||||||
|
isEmpty: false,
|
||||||
|
loc: mockLoc
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NodeTypes.IF_BRANCH,
|
||||||
|
condition: createExpression('bar', false, mockLoc),
|
||||||
|
loc: mockLoc,
|
||||||
|
isRoot: true,
|
||||||
|
children: [createExpression(`bye`, false, mockLoc, true)]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NodeTypes.IF_BRANCH,
|
||||||
|
condition: undefined,
|
||||||
|
loc: mockLoc,
|
||||||
|
isRoot: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: NodeTypes.COMMENT,
|
||||||
|
content: 'foo',
|
||||||
|
loc: mockLoc
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(code).toMatch(`
|
||||||
|
return (foo)
|
||||||
|
? "foo"
|
||||||
|
: (bar)
|
||||||
|
? ${TO_STRING}(bye)
|
||||||
|
: ${CREATE_VNODE}(${COMMENT}, 0, "foo")`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('forNode', () => {
|
||||||
|
const { code } = generate(
|
||||||
|
createRoot({
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: NodeTypes.FOR,
|
||||||
|
loc: mockLoc,
|
||||||
|
source: createExpression(`list`, false, mockLoc),
|
||||||
|
valueAlias: createExpression(`v`, false, mockLoc),
|
||||||
|
keyAlias: createExpression(`k`, false, mockLoc),
|
||||||
|
objectIndexAlias: createExpression(`i`, false, mockLoc),
|
||||||
|
children: [createExpression(`v`, false, mockLoc, true)]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(code).toMatch(`renderList(list, (v, k, i) => toString(v))`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('callExpression + objectExpression + arrayExpression', () => {
|
||||||
|
const { code } = generate(
|
||||||
|
createRoot({
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: NodeTypes.ELEMENT,
|
||||||
|
loc: mockLoc,
|
||||||
|
ns: Namespaces.HTML,
|
||||||
|
tag: 'div',
|
||||||
|
tagType: ElementTypes.ELEMENT,
|
||||||
|
isSelfClosing: false,
|
||||||
|
props: [],
|
||||||
|
children: [],
|
||||||
|
codegenNode: {
|
||||||
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
loc: mockLoc,
|
||||||
|
callee: CREATE_VNODE,
|
||||||
|
arguments: [
|
||||||
|
`"div"`,
|
||||||
|
createObjectExpression(
|
||||||
|
[
|
||||||
|
createObjectProperty(
|
||||||
|
createExpression(`id`, true, mockLoc),
|
||||||
|
createExpression(`foo`, true, mockLoc),
|
||||||
|
mockLoc
|
||||||
|
),
|
||||||
|
createObjectProperty(
|
||||||
|
createExpression(`prop`, false, mockLoc),
|
||||||
|
createExpression(`bar`, false, mockLoc),
|
||||||
|
mockLoc
|
||||||
|
)
|
||||||
|
],
|
||||||
|
mockLoc
|
||||||
|
),
|
||||||
|
createArrayExpression(
|
||||||
|
[
|
||||||
|
'foo',
|
||||||
|
{
|
||||||
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
loc: mockLoc,
|
||||||
|
callee: CREATE_VNODE,
|
||||||
|
arguments: [`"p"`]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
mockLoc
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(code).toMatch(`
|
||||||
|
return ${CREATE_VNODE}("div", {
|
||||||
|
id: "foo",
|
||||||
|
[prop]: bar
|
||||||
|
}, [
|
||||||
|
foo,
|
||||||
|
${CREATE_VNODE}("p")
|
||||||
|
])`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
test('basic source map support', async () => {
|
test('basic source map support', async () => {
|
||||||
const source = `hello {{ world }}`
|
const source = `hello {{ world }}`
|
||||||
const ast = parse(source)
|
const ast = parse(source)
|
||||||
const { code, map } = generate(ast, {
|
const { code, map } = generate(ast, {
|
||||||
|
sourceMap: true,
|
||||||
filename: `foo.vue`
|
filename: `foo.vue`
|
||||||
})
|
})
|
||||||
expect(code).toBe(
|
expect(code).toMatch(
|
||||||
`return function render() {
|
`return function render() {
|
||||||
with (this) {
|
with (this) {
|
||||||
return [
|
return [
|
||||||
|
1
packages/compiler-core/__tests__/compile.spec.ts
Normal file
1
packages/compiler-core/__tests__/compile.spec.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
// Integration tests for parser + transform + codegen
|
@ -2,6 +2,7 @@ import { parse } from '../src/parse'
|
|||||||
import { transform, NodeTransform } from '../src/transform'
|
import { transform, NodeTransform } from '../src/transform'
|
||||||
import { ElementNode, NodeTypes } from '../src/ast'
|
import { ElementNode, NodeTypes } from '../src/ast'
|
||||||
import { ErrorCodes, createCompilerError } from '../src/errors'
|
import { ErrorCodes, createCompilerError } from '../src/errors'
|
||||||
|
import { TO_STRING, CREATE_VNODE, COMMENT } from '../src/runtimeConstants'
|
||||||
|
|
||||||
describe('compiler: transform', () => {
|
describe('compiler: transform', () => {
|
||||||
test('context state', () => {
|
test('context state', () => {
|
||||||
@ -180,4 +181,17 @@ describe('compiler: transform', () => {
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should inject toString helper for interpolations', () => {
|
||||||
|
const ast = parse(`{{ foo }}`)
|
||||||
|
transform(ast, {})
|
||||||
|
expect(ast.imports).toContain(TO_STRING)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should inject createVNode and Comment for comments', () => {
|
||||||
|
const ast = parse(`<!--foo-->`)
|
||||||
|
transform(ast, {})
|
||||||
|
expect(ast.imports).toContain(CREATE_VNODE)
|
||||||
|
expect(ast.imports).toContain(COMMENT)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
// TODO
|
@ -29,7 +29,9 @@ describe('compiler: transform v-if', () => {
|
|||||||
test('basic v-if', () => {
|
test('basic v-if', () => {
|
||||||
const node = parseWithIfTransform(`<div v-if="ok"/>`)
|
const node = parseWithIfTransform(`<div v-if="ok"/>`)
|
||||||
expect(node.type).toBe(NodeTypes.IF)
|
expect(node.type).toBe(NodeTypes.IF)
|
||||||
|
expect(node.isRoot).toBe(true)
|
||||||
expect(node.branches.length).toBe(1)
|
expect(node.branches.length).toBe(1)
|
||||||
|
expect(node.branches[0].isRoot).toBe(true)
|
||||||
expect(node.branches[0].condition!.content).toBe(`ok`)
|
expect(node.branches[0].condition!.content).toBe(`ok`)
|
||||||
expect(node.branches[0].children.length).toBe(1)
|
expect(node.branches[0].children.length).toBe(1)
|
||||||
expect(node.branches[0].children[0].type).toBe(NodeTypes.ELEMENT)
|
expect(node.branches[0].children[0].type).toBe(NodeTypes.ELEMENT)
|
||||||
|
1
packages/compiler-core/__tests__/transforms/vOn.spec.ts
Normal file
1
packages/compiler-core/__tests__/transforms/vOn.spec.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
// TODO
|
@ -117,12 +117,14 @@ export interface ExpressionNode extends Node {
|
|||||||
export interface IfNode extends Node {
|
export interface IfNode extends Node {
|
||||||
type: NodeTypes.IF
|
type: NodeTypes.IF
|
||||||
branches: IfBranchNode[]
|
branches: IfBranchNode[]
|
||||||
|
isRoot: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IfBranchNode extends Node {
|
export interface IfBranchNode extends Node {
|
||||||
type: NodeTypes.IF_BRANCH
|
type: NodeTypes.IF_BRANCH
|
||||||
condition: ExpressionNode | undefined // else
|
condition: ExpressionNode | undefined // else
|
||||||
children: ChildNode[]
|
children: ChildNode[]
|
||||||
|
isRoot: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ForNode extends Node {
|
export interface ForNode extends Node {
|
||||||
@ -203,14 +205,15 @@ export function createObjectProperty(
|
|||||||
export function createExpression(
|
export function createExpression(
|
||||||
content: string,
|
content: string,
|
||||||
isStatic: boolean,
|
isStatic: boolean,
|
||||||
loc: SourceLocation
|
loc: SourceLocation,
|
||||||
|
isInterpolation = false
|
||||||
): ExpressionNode {
|
): ExpressionNode {
|
||||||
return {
|
return {
|
||||||
type: NodeTypes.EXPRESSION,
|
type: NodeTypes.EXPRESSION,
|
||||||
loc,
|
loc,
|
||||||
content,
|
content,
|
||||||
isStatic,
|
isStatic,
|
||||||
isInterpolation: false
|
isInterpolation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,17 +17,33 @@ import {
|
|||||||
import { SourceMapGenerator, RawSourceMap } from 'source-map'
|
import { SourceMapGenerator, RawSourceMap } from 'source-map'
|
||||||
import { advancePositionWithMutation, assert } from './utils'
|
import { advancePositionWithMutation, assert } from './utils'
|
||||||
import { isString, isArray } from '@vue/shared'
|
import { isString, isArray } from '@vue/shared'
|
||||||
import { RENDER_LIST, TO_STRING } from './runtimeConstants'
|
import {
|
||||||
|
RENDER_LIST,
|
||||||
|
TO_STRING,
|
||||||
|
CREATE_VNODE,
|
||||||
|
COMMENT
|
||||||
|
} from './runtimeConstants'
|
||||||
|
|
||||||
type CodegenNode = ChildNode | JSChildNode
|
type CodegenNode = ChildNode | JSChildNode
|
||||||
|
|
||||||
export interface CodegenOptions {
|
export interface CodegenOptions {
|
||||||
// will generate import statements for
|
// - Module mode will generate ES module import statements for helpers
|
||||||
// runtime helpers; otherwise will grab the helpers from global `Vue`.
|
// and export the render function as the default export.
|
||||||
// default: false
|
// - Function mode will generate a single `const { helpers... } = Vue`
|
||||||
|
// statement and return the render function. It is meant to be used with
|
||||||
|
// `new Function(code)()` to generate a render function at runtime.
|
||||||
|
// Default: 'function'
|
||||||
mode?: 'module' | 'function'
|
mode?: 'module' | 'function'
|
||||||
|
// Prefix suitable identifiers with _ctx.
|
||||||
|
// If this option is false, the generated code will be wrapped in a
|
||||||
|
// `with (this) { ... }` block.
|
||||||
|
// Default: false
|
||||||
prefixIdentifiers?: boolean
|
prefixIdentifiers?: boolean
|
||||||
|
// Generate source map?
|
||||||
|
// Default: false
|
||||||
|
sourceMap?: boolean
|
||||||
// Filename for source map generation.
|
// Filename for source map generation.
|
||||||
|
// Default: `template.vue.html`
|
||||||
filename?: string
|
filename?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,12 +71,14 @@ function createCodegenContext(
|
|||||||
{
|
{
|
||||||
mode = 'function',
|
mode = 'function',
|
||||||
prefixIdentifiers = false,
|
prefixIdentifiers = false,
|
||||||
|
sourceMap = false,
|
||||||
filename = `template.vue.html`
|
filename = `template.vue.html`
|
||||||
}: CodegenOptions
|
}: CodegenOptions
|
||||||
): CodegenContext {
|
): CodegenContext {
|
||||||
const context: CodegenContext = {
|
const context: CodegenContext = {
|
||||||
mode,
|
mode,
|
||||||
prefixIdentifiers,
|
prefixIdentifiers,
|
||||||
|
sourceMap,
|
||||||
filename,
|
filename,
|
||||||
source: ast.loc.source,
|
source: ast.loc.source,
|
||||||
code: ``,
|
code: ``,
|
||||||
@ -70,7 +88,8 @@ function createCodegenContext(
|
|||||||
indentLevel: 0,
|
indentLevel: 0,
|
||||||
|
|
||||||
// lazy require source-map implementation, only in non-browser builds!
|
// lazy require source-map implementation, only in non-browser builds!
|
||||||
map: __BROWSER__
|
map:
|
||||||
|
__BROWSER__ || !sourceMap
|
||||||
? undefined
|
? undefined
|
||||||
: new (require('source-map')).SourceMapGenerator(),
|
: new (require('source-map')).SourceMapGenerator(),
|
||||||
|
|
||||||
@ -108,8 +127,8 @@ function createCodegenContext(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const newline = (n: number) => context.push('\n' + ` `.repeat(n))
|
const newline = (n: number) => context.push('\n' + ` `.repeat(n))
|
||||||
if (!__BROWSER__) {
|
if (!__BROWSER__ && context.map) {
|
||||||
context.map!.setSourceContent(filename, context.source)
|
context.map.setSourceContent(filename, context.source)
|
||||||
}
|
}
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
@ -174,6 +193,9 @@ function genChildren(
|
|||||||
context: CodegenContext,
|
context: CodegenContext,
|
||||||
asRoot: boolean = false
|
asRoot: boolean = false
|
||||||
) {
|
) {
|
||||||
|
if (!children.length) {
|
||||||
|
return context.push(`null`)
|
||||||
|
}
|
||||||
const child = children[0]
|
const child = children[0]
|
||||||
if (
|
if (
|
||||||
children.length === 1 &&
|
children.length === 1 &&
|
||||||
@ -321,7 +343,12 @@ function genCompoundExpression(node: ExpressionNode, context: CodegenContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function genComment(node: CommentNode, context: CodegenContext) {
|
function genComment(node: CommentNode, context: CodegenContext) {
|
||||||
context.push(`<!--${node.content}-->`, node)
|
if (__DEV__) {
|
||||||
|
context.push(
|
||||||
|
`${CREATE_VNODE}(${COMMENT}, 0, ${JSON.stringify(node.content)})`,
|
||||||
|
node
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// control flow
|
// control flow
|
||||||
@ -330,7 +357,7 @@ function genIf(node: IfNode, context: CodegenContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function genIfBranch(
|
function genIfBranch(
|
||||||
{ condition, children }: IfBranchNode,
|
{ condition, children, isRoot }: IfBranchNode,
|
||||||
branches: IfBranchNode[],
|
branches: IfBranchNode[],
|
||||||
nextIndex: number,
|
nextIndex: number,
|
||||||
context: CodegenContext
|
context: CodegenContext
|
||||||
@ -344,7 +371,7 @@ function genIfBranch(
|
|||||||
indent()
|
indent()
|
||||||
context.indentLevel++
|
context.indentLevel++
|
||||||
push(`? `)
|
push(`? `)
|
||||||
genChildren(children, context)
|
genChildren(children, context, isRoot)
|
||||||
context.indentLevel--
|
context.indentLevel--
|
||||||
newline()
|
newline()
|
||||||
push(`: `)
|
push(`: `)
|
||||||
@ -357,7 +384,7 @@ function genIfBranch(
|
|||||||
} else {
|
} else {
|
||||||
// v-else
|
// v-else
|
||||||
__DEV__ && assert(nextIndex === branches.length)
|
__DEV__ && assert(nextIndex === branches.length)
|
||||||
genChildren(children, context)
|
genChildren(children, context, isRoot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
// Name mapping constants for runtime helpers that need to be imported in
|
// Name mapping constants for runtime helpers that need to be imported in
|
||||||
// generated code. Make sure these are correctly exported in the runtime!
|
// generated code. Make sure these are correctly exported in the runtime!
|
||||||
|
export const FRAGMENT = `Fragment`
|
||||||
|
export const PORTAL = `Portal`
|
||||||
|
export const COMMENT = `Comment`
|
||||||
|
export const TEXT = `Text`
|
||||||
|
export const SUSPENSE = `Suspense`
|
||||||
export const CREATE_VNODE = `createVNode`
|
export const CREATE_VNODE = `createVNode`
|
||||||
export const RESOLVE_COMPONENT = `resolveComponent`
|
export const RESOLVE_COMPONENT = `resolveComponent`
|
||||||
export const RESOLVE_DIRECTIVE = `resolveDirective`
|
export const RESOLVE_DIRECTIVE = `resolveDirective`
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
} from './ast'
|
} from './ast'
|
||||||
import { isString, isArray } from '@vue/shared'
|
import { isString, isArray } from '@vue/shared'
|
||||||
import { CompilerError, defaultOnError } from './errors'
|
import { CompilerError, defaultOnError } from './errors'
|
||||||
import { TO_STRING } from './runtimeConstants'
|
import { TO_STRING, COMMENT, CREATE_VNODE } from './runtimeConstants'
|
||||||
|
|
||||||
// There are two types of transforms:
|
// There are two types of transforms:
|
||||||
//
|
//
|
||||||
@ -49,11 +49,11 @@ export interface TransformOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface TransformContext extends Required<TransformOptions> {
|
export interface TransformContext extends Required<TransformOptions> {
|
||||||
|
root: RootNode
|
||||||
imports: Set<string>
|
imports: Set<string>
|
||||||
statements: string[]
|
statements: string[]
|
||||||
identifiers: { [name: string]: number | undefined }
|
identifiers: { [name: string]: number | undefined }
|
||||||
parent: ParentNode
|
parent: ParentNode
|
||||||
ancestors: ParentNode[]
|
|
||||||
childIndex: number
|
childIndex: number
|
||||||
currentNode: ChildNode | null
|
currentNode: ChildNode | null
|
||||||
replaceNode(node: ChildNode): void
|
replaceNode(node: ChildNode): void
|
||||||
@ -73,6 +73,7 @@ function createTransformContext(
|
|||||||
}: TransformOptions
|
}: TransformOptions
|
||||||
): TransformContext {
|
): TransformContext {
|
||||||
const context: TransformContext = {
|
const context: TransformContext = {
|
||||||
|
root,
|
||||||
imports: new Set(),
|
imports: new Set(),
|
||||||
statements: [],
|
statements: [],
|
||||||
identifiers: {},
|
identifiers: {},
|
||||||
@ -81,7 +82,6 @@ function createTransformContext(
|
|||||||
directiveTransforms,
|
directiveTransforms,
|
||||||
onError,
|
onError,
|
||||||
parent: root,
|
parent: root,
|
||||||
ancestors: [],
|
|
||||||
childIndex: 0,
|
childIndex: 0,
|
||||||
currentNode: null,
|
currentNode: null,
|
||||||
replaceNode(node) {
|
replaceNode(node) {
|
||||||
@ -139,7 +139,6 @@ export function traverseChildren(
|
|||||||
parent: ParentNode,
|
parent: ParentNode,
|
||||||
context: TransformContext
|
context: TransformContext
|
||||||
) {
|
) {
|
||||||
const ancestors = context.ancestors.concat(parent)
|
|
||||||
let i = 0
|
let i = 0
|
||||||
const nodeRemoved = () => {
|
const nodeRemoved = () => {
|
||||||
i--
|
i--
|
||||||
@ -149,7 +148,6 @@ export function traverseChildren(
|
|||||||
if (isString(child)) continue
|
if (isString(child)) continue
|
||||||
context.currentNode = child
|
context.currentNode = child
|
||||||
context.parent = parent
|
context.parent = parent
|
||||||
context.ancestors = ancestors
|
|
||||||
context.childIndex = i
|
context.childIndex = i
|
||||||
context.onNodeRemoved = nodeRemoved
|
context.onNodeRemoved = nodeRemoved
|
||||||
traverseNode(child, context)
|
traverseNode(child, context)
|
||||||
@ -180,6 +178,12 @@ export function traverseNode(node: ChildNode, context: TransformContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (node.type) {
|
switch (node.type) {
|
||||||
|
case NodeTypes.COMMENT:
|
||||||
|
context.imports.add(CREATE_VNODE)
|
||||||
|
// inject import for the Comment symbol, which is needed for creating
|
||||||
|
// comment nodes with `createVNode`
|
||||||
|
context.imports.add(COMMENT)
|
||||||
|
break
|
||||||
case NodeTypes.EXPRESSION:
|
case NodeTypes.EXPRESSION:
|
||||||
// no need to traverse, but we need to inject toString helper
|
// no need to traverse, but we need to inject toString helper
|
||||||
if (node.isInterpolation) {
|
if (node.isInterpolation) {
|
||||||
|
@ -19,10 +19,14 @@ export const transformIf = createStructuralDirectiveTransform(
|
|||||||
processExpression(dir.exp, context)
|
processExpression(dir.exp, context)
|
||||||
}
|
}
|
||||||
if (dir.name === 'if') {
|
if (dir.name === 'if') {
|
||||||
|
// check if this v-if is root - so that in codegen we can avoid generating
|
||||||
|
// arrays for each branch
|
||||||
|
const isRoot = context.parent === context.root
|
||||||
context.replaceNode({
|
context.replaceNode({
|
||||||
type: NodeTypes.IF,
|
type: NodeTypes.IF,
|
||||||
loc: node.loc,
|
loc: node.loc,
|
||||||
branches: [createIfBranch(node, dir)]
|
branches: [createIfBranch(node, dir, isRoot)],
|
||||||
|
isRoot
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// locate the adjacent v-if
|
// locate the adjacent v-if
|
||||||
@ -39,7 +43,7 @@ export const transformIf = createStructuralDirectiveTransform(
|
|||||||
if (sibling && sibling.type === NodeTypes.IF) {
|
if (sibling && sibling.type === NodeTypes.IF) {
|
||||||
// move the node to the if node's branches
|
// move the node to the if node's branches
|
||||||
context.removeNode()
|
context.removeNode()
|
||||||
const branch = createIfBranch(node, dir)
|
const branch = createIfBranch(node, dir, sibling.isRoot)
|
||||||
if (__DEV__ && comments.length) {
|
if (__DEV__ && comments.length) {
|
||||||
branch.children = [...comments, ...branch.children]
|
branch.children = [...comments, ...branch.children]
|
||||||
}
|
}
|
||||||
@ -63,11 +67,16 @@ export const transformIf = createStructuralDirectiveTransform(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
function createIfBranch(node: ElementNode, dir: DirectiveNode): IfBranchNode {
|
function createIfBranch(
|
||||||
|
node: ElementNode,
|
||||||
|
dir: DirectiveNode,
|
||||||
|
isRoot: boolean
|
||||||
|
): IfBranchNode {
|
||||||
return {
|
return {
|
||||||
type: NodeTypes.IF_BRANCH,
|
type: NodeTypes.IF_BRANCH,
|
||||||
loc: node.loc,
|
loc: node.loc,
|
||||||
condition: dir.name === 'else' ? undefined : dir.exp,
|
condition: dir.name === 'else' ? undefined : dir.exp,
|
||||||
children: node.tagType === ElementTypes.TEMPLATE ? node.children : [node]
|
children: node.tagType === ElementTypes.TEMPLATE ? node.children : [node],
|
||||||
|
isRoot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user