refactor: codegen indent

This commit is contained in:
三咲智子 Kevin Deng 2023-12-01 22:45:08 +08:00
parent 9843175c2c
commit 919644faaf
No known key found for this signature in database
GPG Key ID: 69992F2250DFD93E
4 changed files with 299 additions and 345 deletions

View File

@ -2,345 +2,320 @@
exports[`compile > bindings 1`] = ` exports[`compile > bindings 1`] = `
"export function render(_ctx) { "export function render(_ctx) {
const t0 = _template('<div>count is <!>.</div>'); const t0 = _template(\\"<div>count is <!>.</div>\\")
const n0 = t0(); const n0 = t0()
const { const { 0: [n3, { 1: [n2],}],} = _children(n0)
0: [ const n1 = _createTextNode(count.value)
n3, _insert(n1, n3, n2)
{
1: [n2],
},
],
} = _children(n0);
const n1 = _createTextNode(count.value);
_insert(n1, n3, n2);
_effect(() => { _effect(() => {
_setText(n1, undefined, count.value); _setText(n1, undefined, count.value)
}); })
return n0; return n0
} }
import { template as _template, children as _children, createTextNode as _createTextNode, insert as _insert, effect as _effect, setText as _setText } from 'vue/vapor';
import { template as _template, children as _children, createTextNode as _createTextNode, insert as _insert, effect as _effect, setText as _setText } from 'vue/vapor'
" "
`; `;
exports[`compile > directives > v-bind > simple expression 1`] = ` exports[`compile > directives > v-bind > simple expression 1`] = `
"export function render(_ctx) { "export function render(_ctx) {
const t0 = _template('<div></div>'); const t0 = _template(\\"<div></div>\\")
const n0 = t0(); const n0 = t0()
const { const { 0: [n1],} = _children(n0)
0: [n1],
} = _children(n0);
_effect(() => { _effect(() => {
_setAttr(n1, 'id', undefined, id.value); _setAttr(n1, \\"id\\", undefined, id.value)
}); })
return n0; return n0
} }
import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor';
import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor'
" "
`; `;
exports[`compile > directives > v-html > no expression 1`] = ` exports[`compile > directives > v-html > no expression 1`] = `
"export function render(_ctx) { "export function render(_ctx) {
const t0 = _template('<div></div>'); const t0 = _template(\\"<div></div>\\")
const n0 = t0(); const n0 = t0()
const { const { 0: [n1],} = _children(n0)
0: [n1],
} = _children(n0);
_effect(() => { _effect(() => {
_setHtml(n1, undefined, ''); _setHtml(n1, undefined, \\"\\")
}); })
return n0; return n0
} }
import { template as _template, children as _children, effect as _effect, setHtml as _setHtml } from 'vue/vapor';
import { template as _template, children as _children, effect as _effect, setHtml as _setHtml } from 'vue/vapor'
" "
`; `;
exports[`compile > directives > v-html > simple expression 1`] = ` exports[`compile > directives > v-html > simple expression 1`] = `
"export function render(_ctx) { "export function render(_ctx) {
const t0 = _template('<div></div>'); const t0 = _template(\\"<div></div>\\")
const n0 = t0(); const n0 = t0()
const { const { 0: [n1],} = _children(n0)
0: [n1],
} = _children(n0);
_effect(() => { _effect(() => {
_setHtml(n1, undefined, code.value); _setHtml(n1, undefined, code.value)
}); })
return n0; return n0
} }
import { template as _template, children as _children, effect as _effect, setHtml as _setHtml } from 'vue/vapor';
import { template as _template, children as _children, effect as _effect, setHtml as _setHtml } from 'vue/vapor'
" "
`; `;
exports[`compile > directives > v-on > event modifier 1`] = ` exports[`compile > directives > v-on > event modifier 1`] = `
"export function render(_ctx) { "export function render(_ctx) {
const t0 = _template('<div></div>'); const t0 = _template(\\"<div></div>\\")
const n0 = t0(); const n0 = t0()
const { const { 0: [n1],} = _children(n0)
0: [n1],
} = _children(n0);
_effect(() => { _effect(() => {
_on(n1, 'click', _withModifiers(handleClick, ['prevent', 'stop'])); _on(n1, \\"click\\", _withModifiers(handleClick, [\\"prevent\\", \\"stop\\"]))
}); })
return n0; return n0
} }
import { template as _template, children as _children, effect as _effect, withModifiers as _withModifiers, on as _on } from 'vue/vapor';
import { template as _template, children as _children, effect as _effect, withModifiers as _withModifiers, on as _on } from 'vue/vapor'
" "
`; `;
exports[`compile > directives > v-on > simple expression 1`] = ` exports[`compile > directives > v-on > simple expression 1`] = `
"export function render(_ctx) { "export function render(_ctx) {
const t0 = _template('<div></div>'); const t0 = _template(\\"<div></div>\\")
const n0 = t0(); const n0 = t0()
const { const { 0: [n1],} = _children(n0)
0: [n1],
} = _children(n0);
_effect(() => { _effect(() => {
_on(n1, 'click', handleClick); _on(n1, \\"click\\", handleClick)
}); })
return n0; return n0
} }
import { template as _template, children as _children, effect as _effect, on as _on } from 'vue/vapor';
import { template as _template, children as _children, effect as _effect, on as _on } from 'vue/vapor'
" "
`; `;
exports[`compile > directives > v-once > as root node 1`] = ` exports[`compile > directives > v-once > as root node 1`] = `
"export function render(_ctx) { "export function render(_ctx) {
const t0 = _template('<div></div>'); const t0 = _template(\\"<div></div>\\")
const n0 = t0(); const n0 = t0()
const { const { 0: [n1],} = _children(n0)
0: [n1], _setAttr(n1, \\"id\\", undefined, foo)
} = _children(n0); return n0
_setAttr(n1, 'id', undefined, foo);
return n0;
} }
import { template as _template, children as _children, setAttr as _setAttr } from 'vue/vapor';
import { template as _template, children as _children, setAttr as _setAttr } from 'vue/vapor'
" "
`; `;
exports[`compile > directives > v-once > basic 1`] = ` exports[`compile > directives > v-once > basic 1`] = `
"export function render(_ctx) { "export function render(_ctx) {
const t0 = _template('<div> <span></span></div>'); const t0 = _template(\\"<div> <span></span></div>\\")
const n0 = t0(); const n0 = t0()
const { const { 0: [n3, { 1: [n2],}],} = _children(n0)
0: [ const n1 = _createTextNode(msg.value)
n3, _setText(n1, undefined, msg.value)
{ _setAttr(n2, \\"class\\", undefined, clz.value)
1: [n2], _prepend(n3, n1)
}, return n0
],
} = _children(n0);
const n1 = _createTextNode(msg.value);
_setText(n1, undefined, msg.value);
_setAttr(n2, 'class', undefined, clz.value);
_prepend(n3, n1);
return n0;
} }
import { template as _template, children as _children, createTextNode as _createTextNode, setText as _setText, setAttr as _setAttr, prepend as _prepend } from 'vue/vapor';
import { template as _template, children as _children, createTextNode as _createTextNode, setText as _setText, setAttr as _setAttr, prepend as _prepend } from 'vue/vapor'
" "
`; `;
exports[`compile > directives > v-pre > basic 1`] = ` exports[`compile > directives > v-pre > basic 1`] = `
"export function render(_ctx) { "export function render(_ctx) {
const t0 = _template('<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div>'); const t0 = _template(\\"<div :id=\\\\\\"foo\\\\\\"><Comp></Comp>{{ bar }}</div>\\")
const n0 = t0(); const n0 = t0()
return n0; return n0
} }
import { template as _template } from 'vue/vapor';
import { template as _template } from 'vue/vapor'
" "
`; `;
exports[`compile > directives > v-pre > self-closing v-pre 1`] = ` exports[`compile > directives > v-pre > self-closing v-pre 1`] = `
"export function render(_ctx) { "export function render(_ctx) {
const t0 = _template('<div></div><div><Comp></Comp></div>'); const t0 = _template(\\"<div></div><div><Comp></Comp></div>\\")
const n0 = t0(); const n0 = t0()
const { const { 1: [n1],} = _children(n0)
1: [n1], const n2 = _createTextNode(bar)
} = _children(n0); _append(n1, n2)
const n2 = _createTextNode(bar);
_append(n1, n2);
_effect(() => { _effect(() => {
_setAttr(n1, 'id', undefined, foo); _setAttr(n1, \\"id\\", undefined, foo)
}); })
_effect(() => { _effect(() => {
_setText(n2, undefined, bar); _setText(n2, undefined, bar)
}); })
return n0; return n0
} }
import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, effect as _effect, setAttr as _setAttr, setText as _setText } from 'vue/vapor';
import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, effect as _effect, setAttr as _setAttr, setText as _setText } from 'vue/vapor'
" "
`; `;
exports[`compile > directives > v-pre > should not affect siblings after it 1`] = ` exports[`compile > directives > v-pre > should not affect siblings after it 1`] = `
"export function render(_ctx) { "export function render(_ctx) {
const t0 = _template('<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div><div><Comp></Comp></div>'); const t0 = _template(\\"<div :id=\\\\\\"foo\\\\\\"><Comp></Comp>{{ bar }}</div><div><Comp></Comp></div>\\")
const n0 = t0(); const n0 = t0()
const { const { 1: [n1],} = _children(n0)
1: [n1], const n2 = _createTextNode(bar.value)
} = _children(n0); _append(n1, n2)
const n2 = _createTextNode(bar.value);
_append(n1, n2);
_effect(() => { _effect(() => {
_setAttr(n1, 'id', undefined, foo.value); _setAttr(n1, \\"id\\", undefined, foo.value)
}); })
_effect(() => { _effect(() => {
_setText(n2, undefined, bar.value); _setText(n2, undefined, bar.value)
}); })
return n0; return n0
} }
import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, effect as _effect, setAttr as _setAttr, setText as _setText } from 'vue/vapor';
import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, effect as _effect, setAttr as _setAttr, setText as _setText } from 'vue/vapor'
" "
`; `;
exports[`compile > directives > v-text > no expression 1`] = ` exports[`compile > directives > v-text > no expression 1`] = `
"export function render(_ctx) { "export function render(_ctx) {
const t0 = _template('<div></div>'); const t0 = _template(\\"<div></div>\\")
const n0 = t0(); const n0 = t0()
const { const { 0: [n1],} = _children(n0)
0: [n1],
} = _children(n0);
_effect(() => { _effect(() => {
_setText(n1, undefined, ''); _setText(n1, undefined, \\"\\")
}); })
return n0; return n0
} }
import { template as _template, children as _children, effect as _effect, setText as _setText } from 'vue/vapor';
import { template as _template, children as _children, effect as _effect, setText as _setText } from 'vue/vapor'
" "
`; `;
exports[`compile > directives > v-text > simple expression 1`] = ` exports[`compile > directives > v-text > simple expression 1`] = `
"export function render(_ctx) { "export function render(_ctx) {
const t0 = _template('<div></div>'); const t0 = _template(\\"<div></div>\\")
const n0 = t0(); const n0 = t0()
const { const { 0: [n1],} = _children(n0)
0: [n1],
} = _children(n0);
_effect(() => { _effect(() => {
_setText(n1, undefined, str.value); _setText(n1, undefined, str.value)
}); })
return n0; return n0
} }
import { template as _template, children as _children, effect as _effect, setText as _setText } from 'vue/vapor';
import { template as _template, children as _children, effect as _effect, setText as _setText } from 'vue/vapor'
" "
`; `;
exports[`compile > dynamic root 1`] = ` exports[`compile > dynamic root 1`] = `
"export function render(_ctx) { "export function render(_ctx) {
const t0 = _fragment(); const t0 = _fragment()
const n0 = t0();
const n1 = _createTextNode(1); const n0 = t0()
const n2 = _createTextNode(2); const n1 = _createTextNode(1)
_append(n0, n1, n2); const n2 = _createTextNode(2)
_append(n0, n1, n2)
_effect(() => { _effect(() => {
_setText(n1, undefined, 1); _setText(n1, undefined, 1)
}); })
_effect(() => { _effect(() => {
_setText(n2, undefined, 2); _setText(n2, undefined, 2)
}); })
return n0; return n0
} }
import { fragment as _fragment, createTextNode as _createTextNode, append as _append, effect as _effect, setText as _setText } from 'vue/vapor';
import { fragment as _fragment, createTextNode as _createTextNode, append as _append, effect as _effect, setText as _setText } from 'vue/vapor'
" "
`; `;
exports[`compile > dynamic root nodes and interpolation 1`] = ` exports[`compile > dynamic root nodes and interpolation 1`] = `
"export function render(_ctx) { "export function render(_ctx) {
const t0 = _template('<button>foo<!>foo</button>'); const t0 = _template(\\"<button>foo<!>foo</button>\\")
const n0 = t0(); const n0 = t0()
const { const { 0: [n1, { 1: [n5],}],} = _children(n0)
0: [ const n2 = _createTextNode(count)
n1, const n3 = _createTextNode(count)
{ const n4 = _createTextNode(count)
1: [n5], _prepend(n1, n2)
}, _insert(n3, n1, n5)
], _append(n1, n4)
} = _children(n0);
const n2 = _createTextNode(count);
const n3 = _createTextNode(count);
const n4 = _createTextNode(count);
_prepend(n1, n2);
_insert(n3, n1, n5);
_append(n1, n4);
_effect(() => { _effect(() => {
_on(n1, 'click', handleClick); _on(n1, \\"click\\", handleClick)
}); })
_effect(() => { _effect(() => {
_setAttr(n1, 'id', undefined, count); _setAttr(n1, \\"id\\", undefined, count)
_setText(n2, undefined, count); _setText(n2, undefined, count)
_setText(n3, undefined, count); _setText(n3, undefined, count)
_setText(n4, undefined, count); _setText(n4, undefined, count)
}); })
return n0; return n0
} }
import { template as _template, children as _children, createTextNode as _createTextNode, prepend as _prepend, insert as _insert, append as _append, effect as _effect, on as _on, setAttr as _setAttr, setText as _setText } from 'vue/vapor';
import { template as _template, children as _children, createTextNode as _createTextNode, prepend as _prepend, insert as _insert, append as _append, effect as _effect, on as _on, setAttr as _setAttr, setText as _setText } from 'vue/vapor'
" "
`; `;
exports[`compile > fragment 1`] = ` exports[`compile > fragment 1`] = `
"export function render(_ctx) { "export function render(_ctx) {
const t0 = _template('<p></p><span></span><div></div>'); const t0 = _template(\\"<p></p><span></span><div></div>\\")
const n0 = t0(); const n0 = t0()
return n0; return n0
} }
import { template as _template } from 'vue/vapor';
import { template as _template } from 'vue/vapor'
" "
`; `;
exports[`compile > static + dynamic root 1`] = ` exports[`compile > static + dynamic root 1`] = `
"export function render(_ctx) { "export function render(_ctx) {
const t0 = _template('3<!>6<!>9'); const t0 = _template(\\"3<!>6<!>9\\")
const n0 = t0(); const n0 = t0()
const { const { 1: [n9], 3: [n10],} = _children(n0)
1: [n9], const n1 = _createTextNode(1)
3: [n10], const n2 = _createTextNode(2)
} = _children(n0); const n3 = _createTextNode(4)
const n1 = _createTextNode(1); const n4 = _createTextNode(5)
const n2 = _createTextNode(2); const n5 = _createTextNode(7)
const n3 = _createTextNode(4); const n6 = _createTextNode(8)
const n4 = _createTextNode(5); const n7 = _createTextNode('A')
const n5 = _createTextNode(7); const n8 = _createTextNode('B')
const n6 = _createTextNode(8); _prepend(n0, n1, n2)
const n7 = _createTextNode('A'); _insert([n3, n4], n0, n9)
const n8 = _createTextNode('B'); _insert([n5, n6], n0, n10)
_prepend(n0, n1, n2); _append(n0, n7, n8)
_insert([n3, n4], n0, n9);
_insert([n5, n6], n0, n10);
_append(n0, n7, n8);
_effect(() => { _effect(() => {
_setText(n1, undefined, 1); _setText(n1, undefined, 1)
}); })
_effect(() => { _effect(() => {
_setText(n2, undefined, 2); _setText(n2, undefined, 2)
}); })
_effect(() => { _effect(() => {
_setText(n3, undefined, 4); _setText(n3, undefined, 4)
}); })
_effect(() => { _effect(() => {
_setText(n4, undefined, 5); _setText(n4, undefined, 5)
}); })
_effect(() => { _effect(() => {
_setText(n5, undefined, 7); _setText(n5, undefined, 7)
}); })
_effect(() => { _effect(() => {
_setText(n6, undefined, 8); _setText(n6, undefined, 8)
}); })
_effect(() => { _effect(() => {
_setText(n7, undefined, 'A'); _setText(n7, undefined, 'A')
}); })
_effect(() => { _effect(() => {
_setText(n8, undefined, 'B'); _setText(n8, undefined, 'B')
}); })
return n0; return n0
} }
import { template as _template, children as _children, createTextNode as _createTextNode, prepend as _prepend, insert as _insert, append as _append, effect as _effect, setText as _setText } from 'vue/vapor';
import { template as _template, children as _children, createTextNode as _createTextNode, prepend as _prepend, insert as _insert, append as _append, effect as _effect, setText as _setText } from 'vue/vapor'
" "
`; `;
exports[`compile > static template 1`] = ` exports[`compile > static template 1`] = `
"export function render(_ctx) { "export function render(_ctx) {
const t0 = _template('<div><p>hello</p><input><span></span></div>'); const t0 = _template(\\"<div><p>hello</p><input><span></span></div>\\")
const n0 = t0(); const n0 = t0()
return n0; return n0
} }
import { template as _template } from 'vue/vapor';
import { template as _template } from 'vue/vapor'
" "
`; `;

View File

@ -16,30 +16,31 @@ const increment = () => count.value++
return (() => { return (() => {
const t0 = _template(\\"<h1 id=\\\\\\"title\\\\\\">Counter</h1><p>Count: </p><p>Double: </p><button>Increment</button><div></div><input type=\\\\\\"text\\\\\\"><p>once: </p><p>{{ count }}</p>\\") const t0 = _template(\\"<h1 id=\\\\\\"title\\\\\\">Counter</h1><p>Count: </p><p>Double: </p><button>Increment</button><div></div><input type=\\\\\\"text\\\\\\"><p>once: </p><p>{{ count }}</p>\\")
const n0 = t0() const n0 = t0()
const { 1: [n2], 2: [n4], 3: [n5], 4: [n6], 6: [n8],} = _children(n0) const { 1: [n2], 2: [n4], 3: [n5], 4: [n6], 6: [n8],} = _children(n0)
const n1 = _createTextNode(count.value) const n1 = _createTextNode(count.value)
_append(n2, n1) _append(n2, n1)
const n3 = _createTextNode(double.value) const n3 = _createTextNode(double.value)
_append(n4, n3) _append(n4, n3)
const n7 = _createTextNode(count.value) const n7 = _createTextNode(count.value)
_setText(n7, undefined, count.value) _setText(n7, undefined, count.value)
_append(n8, n7) _append(n8, n7)
_effect(() => { _effect(() => {
_setText(n1, undefined, count.value) _setText(n1, undefined, count.value)
}) })
_effect(() => { _effect(() => {
_setText(n3, undefined, double.value) _setText(n3, undefined, double.value)
}) })
_effect(() => { _effect(() => {
_on(n5, \\"click\\", increment) _on(n5, \\"click\\", increment)
}) })
_effect(() => { _effect(() => {
_setHtml(n6, undefined, html) _setHtml(n6, undefined, html)
}) })
return n0 return n0
})() })()
import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, setText as _setText, effect as _effect, on as _on, setHtml as _setHtml } from 'vue/vapor' import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, setText as _setText, effect as _effect, on as _on, setHtml as _setHtml } from 'vue/vapor'
} }

View File

@ -1,19 +1,8 @@
import { type RootNode, BindingTypes, ErrorCodes } from '@vue/compiler-dom' import { type RootNode, BindingTypes, ErrorCodes } from '@vue/compiler-dom'
import { type CompilerOptions, compile as _compile } from '../src' import { type CompilerOptions, compile as _compile } from '../src'
// TODO remove it function compile(template: string | RootNode, options: CompilerOptions = {}) {
import { format } from 'prettier'
async function compile(
template: string | RootNode,
options: CompilerOptions = {},
) {
let { code } = _compile(template, options) let { code } = _compile(template, options)
code = await format(code, {
parser: 'babel',
printWidth: 999999,
singleQuote: true,
})
return code return code
} }
@ -203,7 +192,9 @@ describe('compile', () => {
) )
expect(code).toMatchSnapshot() expect(code).toMatchSnapshot()
expect(code).contains('<div :id="foo"><Comp></Comp>{{ bar }}</div>') expect(code).contains(
JSON.stringify('<div :id="foo"><Comp></Comp>{{ bar }}</div>'),
)
expect(code).not.contains('effect') expect(code).not.contains('effect')
}) })

View File

@ -30,9 +30,10 @@ export interface CodegenContext
indentLevel: number indentLevel: number
map?: SourceMapGenerator map?: SourceMapGenerator
push(code: string, newlineIndex?: NewlineType, node?: IRNode): void push(code: string, newlineIndex?: number, node?: IRNode): void
pushWithNewline(code: string, newlineIndex?: number, node?: IRNode): void
indent(): void indent(): void
deindent(withoutNewLine?: boolean): void deindent(): void
newline(): void newline(): void
helpers: Set<string> helpers: Set<string>
@ -90,7 +91,7 @@ function createCodegenContext(
vaporHelpers.add(name) vaporHelpers.add(name)
return `_${name}` return `_${name}`
}, },
push(code, newlineIndex: NewlineType = NewlineType.None, node) { push(code, newlineIndex = NewlineType.None, node) {
context.code += code context.code += code
if (!__BROWSER__ && context.map) { if (!__BROWSER__ && context.map) {
if (node) { if (node) {
@ -144,15 +145,15 @@ function createCodegenContext(
} }
} }
}, },
indent() { pushWithNewline(code, newlineIndex, node) {
newline(++context.indentLevel) context.newline()
context.push(code, newlineIndex, node)
}, },
deindent(withoutNewLine = false) { indent() {
if (withoutNewLine) { ++context.indentLevel
--context.indentLevel },
} else { deindent() {
newline(--context.indentLevel) --context.indentLevel
}
}, },
newline() { newline() {
newline(context.indentLevel) newline(context.indentLevel)
@ -196,74 +197,82 @@ export function generate(
options: CodegenOptions = {}, options: CodegenOptions = {},
): CodegenResult { ): CodegenResult {
const ctx = createCodegenContext(ir, options) const ctx = createCodegenContext(ir, options)
const { push, pushWithNewline, indent, deindent, newline } = ctx
const { vaporHelper, helpers, vaporHelpers } = ctx const { vaporHelper, helpers, vaporHelpers } = ctx
const functionName = 'render' const functionName = 'render'
const isSetupInlined = !!options.inline const isSetupInlined = !!options.inline
if (isSetupInlined) { if (isSetupInlined) {
ctx.push(`(() => {\n`, NewlineType.End) push(`(() => {`)
} else { } else {
ctx.push(`export function ${functionName}(_ctx) {\n`, NewlineType.End) push(`export function ${functionName}(_ctx) {`)
} }
indent()
ir.template.forEach((template, i) => { ir.template.forEach((template, i) => {
if (template.type === IRNodeTypes.TEMPLATE_FACTORY) { if (template.type === IRNodeTypes.TEMPLATE_FACTORY) {
// TODO source map? // TODO source map?
ctx.push( pushWithNewline(
`const t${i} = ${vaporHelper('template')}(${JSON.stringify( `const t${i} = ${vaporHelper('template')}(${JSON.stringify(
template.template, template.template,
)})\n`, )})`,
NewlineType.End,
) )
} else { } else {
// fragment // fragment
ctx.push(`const t0 = ${vaporHelper('fragment')}()\n`, NewlineType.End) pushWithNewline(
`const t0 = ${vaporHelper('fragment')}()\n`,
NewlineType.End,
)
} }
}) })
{ {
ctx.push(`const n${ir.dynamic.id} = t0()\n`, NewlineType.End, ir) pushWithNewline(`const n${ir.dynamic.id} = t0()`)
const children = genChildren(ir.dynamic.children) const children = genChildren(ir.dynamic.children)
if (children) { if (children) {
ctx.push( pushWithNewline(
`const ${children} = ${vaporHelper('children')}(n${ir.dynamic.id})\n`, `const ${children} = ${vaporHelper('children')}(n${ir.dynamic.id})`,
NewlineType.End,
) )
} }
for (const operation of ir.operation) { for (const operation of ir.operation) {
genOperation(operation, ctx).forEach((args) => ctx.push(...args)) genOperation(operation, ctx)
} }
for (const [_expr, operations] of Object.entries(ir.effect)) { for (const [_expr, operations] of Object.entries(ir.effect)) {
ctx.push(`${vaporHelper('effect')}(() => {\n`, NewlineType.End) pushWithNewline(`${vaporHelper('effect')}(() => {`)
indent()
for (const operation of operations) { for (const operation of operations) {
genOperation(operation, ctx).forEach((args) => ctx.push(...args)) genOperation(operation, ctx)
} }
ctx.push('})\n', NewlineType.End) deindent()
pushWithNewline('})')
} }
// TODO multiple-template // TODO multiple-template
// TODO return statement in IR // TODO return statement in IR
ctx.push(`return n${ir.dynamic.id}\n`, NewlineType.End) pushWithNewline(`return n${ir.dynamic.id}`)
} }
deindent()
newline()
if (isSetupInlined) { if (isSetupInlined) {
ctx.push('})()') push('})()')
} else { } else {
ctx.push('}') push('}')
} }
ctx.newline() ctx.newline()
if (vaporHelpers.size) if (vaporHelpers.size)
// TODO: extract // TODO: extract
ctx.push( pushWithNewline(
`import { ${[...vaporHelpers] `import { ${[...vaporHelpers]
.map((h) => `${h} as _${h}`) .map((h) => `${h} as _${h}`)
.join(', ')} } from 'vue/vapor'\n`, .join(', ')} } from 'vue/vapor'\n`,
NewlineType.End, NewlineType.End,
) )
if (helpers.size) if (helpers.size)
ctx.push( pushWithNewline(
`import { ${[...helpers] `import { ${[...helpers]
.map((h) => `${h} as _${h}`) .map((h) => `${h} as _${h}`)
.join(', ')} } from 'vue'\n`, .join(', ')} } from 'vue'\n`,
@ -280,30 +289,24 @@ export function generate(
function genOperation( function genOperation(
oper: OperationNode, oper: OperationNode,
{ vaporHelper }: CodegenContext, { vaporHelper, pushWithNewline }: CodegenContext,
): Parameters<CodegenContext['push']>[] { ) {
// TODO: cache old value // TODO: cache old value
switch (oper.type) { switch (oper.type) {
case IRNodeTypes.SET_PROP: { case IRNodeTypes.SET_PROP: {
return [ pushWithNewline(
[ `${vaporHelper('setAttr')}(n${oper.element}, ${JSON.stringify(
`${vaporHelper('setAttr')}(n${oper.element}, ${JSON.stringify( oper.name,
oper.name, )}, undefined, ${oper.value})`,
)}, undefined, ${oper.value})\n`, )
NewlineType.End, return
],
]
} }
case IRNodeTypes.SET_TEXT: { case IRNodeTypes.SET_TEXT: {
return [ pushWithNewline(
[ `${vaporHelper('setText')}(n${oper.element}, undefined, ${oper.value})`,
`${vaporHelper('setText')}(n${oper.element}, undefined, ${ )
oper.value return
})\n`,
NewlineType.End,
],
]
} }
case IRNodeTypes.SET_EVENT: { case IRNodeTypes.SET_EVENT: {
@ -313,70 +316,54 @@ function genOperation(
oper.modifiers, oper.modifiers,
)})` )})`
} }
return [ pushWithNewline(
[ `${vaporHelper('on')}(n${oper.element}, ${JSON.stringify(
`${vaporHelper('on')}(n${oper.element}, ${JSON.stringify( oper.name,
oper.name, )}, ${value})`,
)}, ${value})\n`, )
NewlineType.End, return
],
]
} }
case IRNodeTypes.SET_HTML: { case IRNodeTypes.SET_HTML: {
return [ pushWithNewline(
[ `${vaporHelper('setHtml')}(n${oper.element}, undefined, ${oper.value})`,
`${vaporHelper('setHtml')}(n${oper.element}, undefined, ${ )
oper.value return
})\n`,
NewlineType.End,
],
]
} }
case IRNodeTypes.CREATE_TEXT_NODE: { case IRNodeTypes.CREATE_TEXT_NODE: {
return [ pushWithNewline(
[ `const n${oper.id} = ${vaporHelper('createTextNode')}(${oper.value})`,
`const n${oper.id} = ${vaporHelper('createTextNode')}(${ )
oper.value return
})\n`,
NewlineType.End,
],
]
} }
case IRNodeTypes.INSERT_NODE: { case IRNodeTypes.INSERT_NODE: {
const elements = ([] as number[]).concat(oper.element) const elements = ([] as number[]).concat(oper.element)
let element = elements.map((el) => `n${el}`).join(', ') let element = elements.map((el) => `n${el}`).join(', ')
if (elements.length > 1) element = `[${element}]` if (elements.length > 1) element = `[${element}]`
return [ pushWithNewline(
[ `${vaporHelper('insert')}(${element}, n${
`${vaporHelper('insert')}(${element}, n${ oper.parent
oper.parent }${`, n${oper.anchor}`})`,
}${`, n${oper.anchor}`})\n`, )
NewlineType.End, return
],
]
} }
case IRNodeTypes.PREPEND_NODE: { case IRNodeTypes.PREPEND_NODE: {
return [ pushWithNewline(
[ `${vaporHelper('prepend')}(n${oper.parent}, ${oper.elements
`${vaporHelper('prepend')}(n${oper.parent}, ${oper.elements .map((el) => `n${el}`)
.map((el) => `n${el}`) .join(', ')})`,
.join(', ')})\n`, )
NewlineType.End, return
],
]
} }
case IRNodeTypes.APPEND_NODE: { case IRNodeTypes.APPEND_NODE: {
return [ pushWithNewline(
[ `${vaporHelper('append')}(n${oper.parent}, ${oper.elements
`${vaporHelper('append')}(n${oper.parent}, ${oper.elements .map((el) => `n${el}`)
.map((el) => `n${el}`) .join(', ')})`,
.join(', ')})\n`, )
NewlineType.End, return
],
]
} }
default: default:
return checkNever(oper) return checkNever(oper)