feat: pushFnCall

This commit is contained in:
三咲智子 Kevin Deng 2023-12-10 01:26:19 +08:00
parent 0c26b0d4ed
commit ecf7da98d7
No known key found for this signature in database
GPG Key ID: 69992F2250DFD93E
1 changed files with 123 additions and 92 deletions

View File

@ -52,7 +52,7 @@ export interface CodegenContext extends Required<CodegenOptions> {
loc?: SourceLocation, loc?: SourceLocation,
name?: string, name?: string,
): void ): void
pushWithNewline( pushNewline(
code: string, code: string,
newlineIndex?: number, newlineIndex?: number,
loc?: SourceLocation, loc?: SourceLocation,
@ -60,8 +60,9 @@ export interface CodegenContext extends Required<CodegenOptions> {
): void ): void
pushMulti( pushMulti(
codes: [left: string, right: string, segment?: string], codes: [left: string, right: string, segment?: string],
...fn: Array<false | (() => void)> ...fn: Array<false | string | (() => void)>
): void ): void
pushFnCall(name: string, ...args: Array<false | string | (() => void)>): void
withIndent(fn: () => void): void withIndent(fn: () => void): void
newline(): void newline(): void
@ -169,7 +170,7 @@ function createCodegenContext(
} }
} }
}, },
pushWithNewline(code, newlineIndex, node) { pushNewline(code, newlineIndex, node) {
context.newline() context.newline()
context.push(code, newlineIndex, node) context.push(code, newlineIndex, node)
}, },
@ -177,25 +178,28 @@ function createCodegenContext(
fns = fns.filter(Boolean) fns = fns.filter(Boolean)
context.push(left) context.push(left)
for (let i = 0; i < fns.length; i++) { for (let i = 0; i < fns.length; i++) {
;(fns[i] as () => void)() const fn = fns[i] as string | (() => void)
if (isString(fn)) context.push(fn)
else fn()
if (seg && i < fns.length - 1) context.push(seg) if (seg && i < fns.length - 1) context.push(seg)
} }
context.push(right) context.push(right)
}, },
pushFnCall(name, ...args) {
context.push(name)
context.pushMulti(['(', ')', ', '], ...args)
},
withIndent(fn) { withIndent(fn) {
++context.indentLevel ++context.indentLevel
fn() fn()
--context.indentLevel --context.indentLevel
}, },
newline() { newline() {
newline(context.indentLevel) context.push(`\n${` `.repeat(context.indentLevel)}`, NewlineType.Start)
}, },
} }
function newline(n: number) {
context.push(`\n${` `.repeat(n)}`, NewlineType.Start)
}
function addMapping(loc: Position, name: string | null = null) { function addMapping(loc: Position, name: string | null = null) {
// we use the private property to directly add the mapping // we use the private property to directly add the mapping
// because the addMapping() implementation in source-map-js has a bunch of // because the addMapping() implementation in source-map-js has a bunch of
@ -231,7 +235,7 @@ export function generate(
const ctx = createCodegenContext(ir, options) const ctx = createCodegenContext(ir, options)
const { const {
push, push,
pushWithNewline, pushNewline,
withIndent, withIndent,
newline, newline,
helpers, helpers,
@ -246,21 +250,21 @@ export function generate(
} else { } else {
// placeholder for preamble // placeholder for preamble
newline() newline()
pushWithNewline(`export function ${functionName}(_ctx) {`) pushNewline(`export function ${functionName}(_ctx) {`)
} }
withIndent(() => { withIndent(() => {
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?
pushWithNewline( pushNewline(
`const t${i} = ${vaporHelper('template')}(${JSON.stringify( `const t${i} = ${vaporHelper('template')}(${JSON.stringify(
template.template, template.template,
)})`, )})`,
) )
} else { } else {
// fragment // fragment
pushWithNewline( pushNewline(
`const t0 = ${vaporHelper('fragment')}()\n`, `const t0 = ${vaporHelper('fragment')}()\n`,
NewlineType.End, NewlineType.End,
) )
@ -268,11 +272,11 @@ export function generate(
}) })
{ {
pushWithNewline(`const n${ir.dynamic.id} = t0()`) pushNewline(`const n${ir.dynamic.id} = t0()`)
const children = genChildren(ir.dynamic.children) const children = genChildren(ir.dynamic.children)
if (children) { if (children) {
pushWithNewline( pushNewline(
`const ${children} = ${vaporHelper('children')}(n${ir.dynamic.id})`, `const ${children} = ${vaporHelper('children')}(n${ir.dynamic.id})`,
) )
} }
@ -289,18 +293,18 @@ export function generate(
} }
for (const { operations } of ir.effect) { for (const { operations } of ir.effect) {
pushWithNewline(`${vaporHelper('effect')}(() => {`) pushNewline(`${vaporHelper('effect')}(() => {`)
withIndent(() => { withIndent(() => {
for (const operation of operations) { for (const operation of operations) {
genOperation(operation, ctx) genOperation(operation, ctx)
} }
}) })
pushWithNewline('})') pushNewline('})')
} }
// TODO multiple-template // TODO multiple-template
// TODO return statement in IR // TODO return statement in IR
pushWithNewline(`return n${ir.dynamic.id}`) pushNewline(`return n${ir.dynamic.id}`)
} }
}) })
@ -336,7 +340,6 @@ export function generate(
function genChildren(children: IRDynamicChildren) { function genChildren(children: IRDynamicChildren) {
let code = '' let code = ''
// TODO
let offset = 0 let offset = 0
for (const [index, child] of Object.entries(children)) { for (const [index, child] of Object.entries(children)) {
const childrenLength = Object.keys(child.children).length const childrenLength = Object.keys(child.children).length
@ -388,77 +391,93 @@ function genOperation(oper: OperationNode, context: CodegenContext) {
} }
function genSetProp(oper: SetPropIRNode, context: CodegenContext) { function genSetProp(oper: SetPropIRNode, context: CodegenContext) {
const { push, pushWithNewline, vaporHelper, helper } = context const { pushFnCall, newline, vaporHelper, helper } = context
pushWithNewline(`${vaporHelper('setAttr')}(n${oper.element}, `)
if (oper.runtimeCamelize) push(`${helper('camelize')}(`) newline()
pushFnCall(
vaporHelper('setAttr'),
`n${oper.element}`,
// 2. key name
() => {
if (oper.runtimeCamelize) {
pushFnCall(helper('camelize'), () => genExpression(oper.key, context))
} else {
genExpression(oper.key, context) genExpression(oper.key, context)
if (oper.runtimeCamelize) push(`)`) }
push(`, undefined, `) },
genExpression(oper.value, context) 'undefined',
push(')') () => genExpression(oper.value, context),
)
} }
function genSetText(oper: SetTextIRNode, context: CodegenContext) { function genSetText(oper: SetTextIRNode, context: CodegenContext) {
const { push, pushWithNewline, vaporHelper } = context const { pushFnCall, newline, vaporHelper } = context
pushWithNewline(`${vaporHelper('setText')}(n${oper.element}, undefined, `) newline()
genExpression(oper.value, context) pushFnCall(vaporHelper('setText'), `n${oper.element}`, 'undefined', () =>
push(')') genExpression(oper.value, context),
)
} }
function genSetHtml(oper: SetHtmlIRNode, context: CodegenContext) { function genSetHtml(oper: SetHtmlIRNode, context: CodegenContext) {
const { push, pushWithNewline, vaporHelper } = context const { newline, pushFnCall, vaporHelper } = context
pushWithNewline(`${vaporHelper('setHtml')}(n${oper.element}, undefined, `) newline()
genExpression(oper.value, context) pushFnCall(vaporHelper('setHtml'), `n${oper.element}`, 'undefined', () =>
push(')') genExpression(oper.value, context),
)
} }
function genCreateTextNode( function genCreateTextNode(
oper: CreateTextNodeIRNode, oper: CreateTextNodeIRNode,
context: CodegenContext, context: CodegenContext,
) { ) {
const { push, pushWithNewline, vaporHelper } = context const { pushNewline, pushFnCall, vaporHelper } = context
pushWithNewline(`const n${oper.id} = ${vaporHelper('createTextNode')}(`) pushNewline(`const n${oper.id} = `)
genExpression(oper.value, context) pushFnCall(vaporHelper('createTextNode'), () =>
push(')') genExpression(oper.value, context),
)
} }
function genInsertNode(oper: InsertNodeIRNode, context: CodegenContext) { function genInsertNode(oper: InsertNodeIRNode, context: CodegenContext) {
const { pushWithNewline, vaporHelper } = context const { newline, pushFnCall, vaporHelper } = context
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}]`
pushWithNewline( newline()
`${vaporHelper('insert')}(${element}, n${ pushFnCall(
oper.parent vaporHelper('insert'),
}${`, n${oper.anchor}`})`, element,
`n${oper.parent}`,
`n${oper.anchor}`,
) )
} }
function genPrependNode(oper: PrependNodeIRNode, context: CodegenContext) { function genPrependNode(oper: PrependNodeIRNode, context: CodegenContext) {
const { pushWithNewline, vaporHelper } = context const { newline, pushFnCall, vaporHelper } = context
pushWithNewline( newline()
`${vaporHelper('prepend')}(n${oper.parent}, ${oper.elements pushFnCall(
.map((el) => `n${el}`) vaporHelper('prepend'),
.join(', ')})`, `n${oper.parent}`,
oper.elements.map((el) => `n${el}`).join(', '),
) )
} }
function genAppendNode(oper: AppendNodeIRNode, context: CodegenContext) { function genAppendNode(oper: AppendNodeIRNode, context: CodegenContext) {
const { pushWithNewline, vaporHelper } = context const { newline, pushFnCall, vaporHelper } = context
pushWithNewline( newline()
`${vaporHelper('append')}(n${oper.parent}, ${oper.elements pushFnCall(
.map((el) => `n${el}`) vaporHelper('append'),
.join(', ')})`, `n${oper.parent}`,
oper.elements.map((el) => `n${el}`).join(', '),
) )
} }
function genSetEvent(oper: SetEventIRNode, context: CodegenContext) { function genSetEvent(oper: SetEventIRNode, context: CodegenContext) {
const { vaporHelper, push, pushWithNewline, pushMulti: pushMulti } = context const { vaporHelper, push, newline, pushMulti, pushFnCall } = context
const { keys, nonKeys, options } = oper.modifiers const { keys, nonKeys, options } = oper.modifiers
pushWithNewline(vaporHelper('on')) newline()
pushMulti( pushFnCall(
['(', ')', ', '], vaporHelper('on'),
// 1st arg: event name // 1st arg: event name
() => push(`n${oper.element}`), () => push(`n${oper.element}`),
// 2nd arg: event name // 2nd arg: event name
@ -508,19 +527,29 @@ function genSetEvent(oper: SetEventIRNode, context: CodegenContext) {
} }
function genWithDirective(oper: WithDirectiveIRNode, context: CodegenContext) { function genWithDirective(oper: WithDirectiveIRNode, context: CodegenContext) {
const { push, pushWithNewline, vaporHelper, bindingMetadata } = context const { push, newline, pushFnCall, pushMulti, vaporHelper, bindingMetadata } =
context
const { dir } = oper const { dir } = oper
// TODO merge directive for the same node // TODO merge directive for the same node
pushWithNewline(`${vaporHelper('withDirectives')}(n${oper.element}, [[`) newline()
pushFnCall(
vaporHelper('withDirectives'),
// 1st arg: node
`n${oper.element}`,
// 2nd arg: directives
() => {
push('[')
// directive
pushMulti(['[', ']', ', '], () => {
if (dir.name === 'show') { if (dir.name === 'show') {
push(vaporHelper('vShow')) push(vaporHelper('vShow'))
} else { } else {
const directiveReference = camelize(`v-${dir.name}`) const directiveReference = camelize(`v-${dir.name}`)
// TODO resolve directive // TODO resolve directive
if (bindingMetadata[directiveReference]) { if (bindingMetadata[directiveReference]) {
const directiveExpression = createSimpleExpression(directiveReference) const directiveExpression =
createSimpleExpression(directiveReference)
directiveExpression.ast = null directiveExpression.ast = null
genExpression(directiveExpression, context) genExpression(directiveExpression, context)
} }
@ -546,8 +575,10 @@ function genWithDirective(oper: WithDirectiveIRNode, context: CodegenContext) {
push(genDirectiveModifiers(dir.modifiers)) push(genDirectiveModifiers(dir.modifiers))
push(' }') push(' }')
} }
push(']])') })
return push(']')
},
)
} }
// TODO: other types (not only string) // TODO: other types (not only string)