diff --git a/packages/compiler-vapor/src/generate.ts b/packages/compiler-vapor/src/generate.ts index c6e5202de..bba27e325 100644 --- a/packages/compiler-vapor/src/generate.ts +++ b/packages/compiler-vapor/src/generate.ts @@ -1,50 +1,36 @@ import { type CodegenOptions as BaseCodegenOptions, - BindingTypes, type CodegenResult, NewlineType, type Position, type SourceLocation, - advancePositionWithClone, advancePositionWithMutation, - createSimpleExpression, - isMemberExpression, - isSimpleIdentifier, locStub, - walkIdentifiers, } from '@vue/compiler-dom' import { - type AppendNodeIRNode, - type CreateTextNodeIRNode, type IRDynamicChildren, - type IRExpression, IRNodeTypes, - type InsertNodeIRNode, type OperationNode, - type PrependNodeIRNode, type RootIRNode, - type SetEventIRNode, - type SetHtmlIRNode, - type SetModelValueIRNode, - type SetPropIRNode, - type SetRefIRNode, - type SetTextIRNode, type VaporHelper, type WithDirectiveIRNode, } from './ir' import { SourceMapGenerator } from 'source-map-js' -import { camelize, isGloballyAllowed, isString, makeMap } from '@vue/shared' -import type { Identifier } from '@babel/types' +import { isString } from '@vue/shared' import type { ParserPlugin } from '@babel/parser' +import { genSetProp } from './generators/prop' +import { genCreateTextNode, genSetText } from './generators/text' +import { genSetEvent } from './generators/event' +import { genSetHtml } from './generators/html' +import { genSetRef } from './generators/ref' +import { genSetModelValue } from './generators/modelValue' +import { genAppendNode, genInsertNode, genPrependNode } from './generators/dom' +import { genWithDirective } from './generators/directive' interface CodegenOptions extends BaseCodegenOptions { expressionPlugins?: ParserPlugin[] } -// TODO: share this with compiler-core -const fnExpRE = - /^\s*([\w$_]+|(async\s*)?\([^)]*?\))\s*(:[^=]+)?=>|^\s*(async\s+)?function(?:\s+[\w$]+)?\s*\(/ - // remove when stable // @ts-expect-error function checkNever(x: never): never {} @@ -407,361 +393,3 @@ function genOperation(oper: OperationNode, context: CodegenContext) { return checkNever(oper) } } - -function genSetProp(oper: SetPropIRNode, context: CodegenContext) { - const { pushFnCall, pushMulti, newline, vaporHelper, helper } = context - - newline() - pushFnCall( - vaporHelper('setDynamicProp'), - `n${oper.element}`, - // 2. key name - () => { - if (oper.runtimeCamelize) { - pushFnCall(helper('camelize'), () => genExpression(oper.key, context)) - } else if (oper.runtimePrefix) { - pushMulti([`\`${oper.runtimePrefix}\${`, `}\``], () => - genExpression(oper.key, context), - ) - } else { - genExpression(oper.key, context) - } - }, - 'undefined', - () => genExpression(oper.value, context), - ) -} - -function genSetText(oper: SetTextIRNode, context: CodegenContext) { - const { pushFnCall, newline, vaporHelper } = context - newline() - pushFnCall(vaporHelper('setText'), `n${oper.element}`, 'undefined', () => - genExpression(oper.value, context), - ) -} - -function genSetHtml(oper: SetHtmlIRNode, context: CodegenContext) { - const { newline, pushFnCall, vaporHelper } = context - newline() - pushFnCall(vaporHelper('setHtml'), `n${oper.element}`, 'undefined', () => - genExpression(oper.value, context), - ) -} - -function genSetRef(oper: SetRefIRNode, context: CodegenContext) { - const { newline, pushFnCall, vaporHelper } = context - newline() - pushFnCall(vaporHelper('setRef'), `n${oper.element}`, () => - genExpression(oper.value, context), - ) -} - -function genSetModelValue(oper: SetModelValueIRNode, context: CodegenContext) { - const { vaporHelper, push, newline, pushFnCall } = context - - newline() - pushFnCall( - vaporHelper('on'), - // 1st arg: event name - () => push(`n${oper.element}`), - // 2nd arg: event name - () => { - if (isString(oper.key)) { - push(JSON.stringify(`update:${camelize(oper.key)}`)) - } else { - push('`update:${') - genExpression(oper.key, context) - push('}`') - } - }, - // 3rd arg: event handler - () => { - push((context.isTS ? `($event: any)` : `$event`) + ' => ((') - // TODO handle not a ref - genExpression(oper.value, context) - push(') = $event)') - }, - ) -} - -function genCreateTextNode( - oper: CreateTextNodeIRNode, - context: CodegenContext, -) { - const { pushNewline, pushFnCall, vaporHelper } = context - pushNewline(`const n${oper.id} = `) - pushFnCall(vaporHelper('createTextNode'), () => - genExpression(oper.value, context), - ) -} - -function genInsertNode(oper: InsertNodeIRNode, context: CodegenContext) { - const { newline, pushFnCall, vaporHelper } = context - const elements = ([] as number[]).concat(oper.element) - let element = elements.map((el) => `n${el}`).join(', ') - if (elements.length > 1) element = `[${element}]` - newline() - pushFnCall( - vaporHelper('insert'), - element, - `n${oper.parent}`, - `n${oper.anchor}`, - ) -} - -function genPrependNode(oper: PrependNodeIRNode, context: CodegenContext) { - const { newline, pushFnCall, vaporHelper } = context - newline() - pushFnCall( - vaporHelper('prepend'), - `n${oper.parent}`, - oper.elements.map((el) => `n${el}`).join(', '), - ) -} - -function genAppendNode(oper: AppendNodeIRNode, context: CodegenContext) { - const { newline, pushFnCall, vaporHelper } = context - newline() - pushFnCall( - vaporHelper('append'), - `n${oper.parent}`, - oper.elements.map((el) => `n${el}`).join(', '), - ) -} - -function genSetEvent(oper: SetEventIRNode, context: CodegenContext) { - const { vaporHelper, push, newline, pushMulti, pushFnCall } = context - const { keys, nonKeys, options } = oper.modifiers - - newline() - pushFnCall( - vaporHelper('on'), - // 1st arg: event name - () => push(`n${oper.element}`), - // 2nd arg: event name - () => { - if (oper.keyOverride) { - const find = JSON.stringify(oper.keyOverride[0]) - const replacement = JSON.stringify(oper.keyOverride[1]) - pushMulti(['(', ')'], () => genExpression(oper.key, context)) - push(` === ${find} ? ${replacement} : `) - pushMulti(['(', ')'], () => genExpression(oper.key, context)) - } else { - genExpression(oper.key, context) - } - }, - // 3rd arg: event handler - () => { - const pushWithKeys = (fn: () => void) => { - push(`${vaporHelper('withKeys')}(`) - fn() - push(`, ${genArrayExpression(keys)})`) - } - const pushWithModifiers = (fn: () => void) => { - push(`${vaporHelper('withModifiers')}(`) - fn() - push(`, ${genArrayExpression(nonKeys)})`) - } - const pushNoop = (fn: () => void) => fn() - - ;(keys.length ? pushWithKeys : pushNoop)(() => - (nonKeys.length ? pushWithModifiers : pushNoop)(() => { - genEventHandler(context) - }), - ) - }, - // 4th arg, gen options - !!options.length && - (() => push(`{ ${options.map((v) => `${v}: true`).join(', ')} }`)), - ) - - function genEventHandler(context: CodegenContext) { - const exp = oper.value - if (exp && exp.content.trim()) { - const isMemberExp = isMemberExpression(exp.content, context) - const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content)) - const hasMultipleStatements = exp.content.includes(`;`) - - if (isInlineStatement) { - push('$event => ') - push(hasMultipleStatements ? '{' : '(') - const knownIds = Object.create(null) - knownIds['$event'] = 1 - genExpression(exp, context, knownIds) - push(hasMultipleStatements ? '}' : ')') - } else if (isMemberExp) { - push('(...args) => (') - genExpression(exp, context) - push(' && ') - genExpression(exp, context) - push('(...args))') - } else { - genExpression(exp, context) - } - } else { - push('() => {}') - } - } -} - -function genWithDirective(oper: WithDirectiveIRNode, context: CodegenContext) { - const { push, newline, pushFnCall, pushMulti, vaporHelper, bindingMetadata } = - context - const { dir, builtin } = oper - - // TODO merge directive for the same node - newline() - pushFnCall( - vaporHelper('withDirectives'), - // 1st arg: node - `n${oper.element}`, - // 2nd arg: directives - () => { - push('[') - // directive - pushMulti(['[', ']', ', '], () => { - if (dir.name === 'show') { - push(vaporHelper('vShow')) - } else if (builtin) { - push(vaporHelper(builtin)) - } else { - const directiveReference = camelize(`v-${dir.name}`) - // TODO resolve directive - if (bindingMetadata[directiveReference]) { - const directiveExpression = - createSimpleExpression(directiveReference) - directiveExpression.ast = null - genExpression(directiveExpression, context) - } - } - - if (dir.exp) { - push(', () => ') - genExpression(dir.exp, context) - } else if (dir.arg || dir.modifiers.length) { - push(', void 0') - } - - if (dir.arg) { - push(', ') - genExpression(dir.arg, context) - } else if (dir.modifiers.length) { - push(', void 0') - } - - if (dir.modifiers.length) { - push(', ') - push('{ ') - push(genDirectiveModifiers(dir.modifiers)) - push(' }') - } - }) - push(']') - }, - ) -} - -// TODO: other types (not only string) -function genArrayExpression(elements: string[]) { - return `[${elements.map((it) => JSON.stringify(it)).join(', ')}]` -} - -const isLiteralWhitelisted = /*#__PURE__*/ makeMap('true,false,null,this') - -function genExpression( - node: IRExpression, - context: CodegenContext, - knownIds: Record = Object.create(null), -): void { - const { push } = context - if (isString(node)) return push(node) - - const { content: rawExpr, ast, isStatic, loc } = node - if (isStatic) { - return push(JSON.stringify(rawExpr), NewlineType.None, loc) - } - if ( - __BROWSER__ || - !context.prefixIdentifiers || - !node.content.trim() || - // there was a parsing error - ast === false || - isGloballyAllowed(rawExpr) || - isLiteralWhitelisted(rawExpr) - ) { - return push(rawExpr, NewlineType.None, loc) - } - - if (ast === null) { - // the expression is a simple identifier - return genIdentifier(rawExpr, context, loc) - } - - const ids: Identifier[] = [] - walkIdentifiers( - ast!, - (id, parent, parentStack, isReference, isLocal) => { - if (isLocal) return - ids.push(id) - }, - false, - [], - knownIds, - ) - if (ids.length) { - ids.sort((a, b) => a.start! - b.start!) - ids.forEach((id, i) => { - // range is offset by -1 due to the wrapping parens when parsed - const start = id.start! - 1 - const end = id.end! - 1 - const last = ids[i - 1] - - const leadingText = rawExpr.slice(last ? last.end! - 1 : 0, start) - if (leadingText.length) push(leadingText, NewlineType.Unknown) - - const source = rawExpr.slice(start, end) - genIdentifier(source, context, { - start: advancePositionWithClone(node.loc.start, source, start), - end: advancePositionWithClone(node.loc.start, source, end), - source, - }) - - if (i === ids.length - 1 && end < rawExpr.length) { - push(rawExpr.slice(end), NewlineType.Unknown) - } - }) - } else { - push(rawExpr, NewlineType.Unknown) - } -} - -function genIdentifier( - id: string, - { inline, bindingMetadata, vaporHelper, push }: CodegenContext, - loc?: SourceLocation, -): void { - let name: string | undefined = id - if (inline) { - switch (bindingMetadata[id]) { - case BindingTypes.SETUP_REF: - name = id += '.value' - break - case BindingTypes.SETUP_MAYBE_REF: - id = `${vaporHelper('unref')}(${id})` - name = undefined - break - } - } else { - id = `_ctx.${id}` - } - push(id, NewlineType.None, loc, name) -} - -function genDirectiveModifiers(modifiers: string[]) { - return modifiers - .map( - (value) => - `${isSimpleIdentifier(value) ? value : JSON.stringify(value)}: true`, - ) - .join(', ') -} diff --git a/packages/compiler-vapor/src/generators/directive.ts b/packages/compiler-vapor/src/generators/directive.ts new file mode 100644 index 000000000..4b54bc5b7 --- /dev/null +++ b/packages/compiler-vapor/src/generators/directive.ts @@ -0,0 +1,74 @@ +import { createSimpleExpression, isSimpleIdentifier } from '@vue/compiler-dom' +import { camelize } from '@vue/shared' +import { genExpression } from './expression' +import type { CodegenContext } from '../generate' +import type { WithDirectiveIRNode } from '../ir' + +export function genWithDirective( + oper: WithDirectiveIRNode, + context: CodegenContext, +) { + const { push, newline, pushFnCall, pushMulti, vaporHelper, bindingMetadata } = + context + const { dir, builtin } = oper + + // TODO merge directive for the same node + newline() + pushFnCall( + vaporHelper('withDirectives'), + // 1st arg: node + `n${oper.element}`, + // 2nd arg: directives + () => { + push('[') + // directive + pushMulti(['[', ']', ', '], () => { + if (dir.name === 'show') { + push(vaporHelper('vShow')) + } else if (builtin) { + push(vaporHelper(builtin)) + } else { + const directiveReference = camelize(`v-${dir.name}`) + // TODO resolve directive + if (bindingMetadata[directiveReference]) { + const directiveExpression = + createSimpleExpression(directiveReference) + directiveExpression.ast = null + genExpression(directiveExpression, context) + } + } + + if (dir.exp) { + push(', () => ') + genExpression(dir.exp, context) + } else if (dir.arg || dir.modifiers.length) { + push(', void 0') + } + + if (dir.arg) { + push(', ') + genExpression(dir.arg, context) + } else if (dir.modifiers.length) { + push(', void 0') + } + + if (dir.modifiers.length) { + push(', ') + push('{ ') + push(genDirectiveModifiers(dir.modifiers)) + push(' }') + } + }) + push(']') + }, + ) +} + +function genDirectiveModifiers(modifiers: string[]) { + return modifiers + .map( + (value) => + `${isSimpleIdentifier(value) ? value : JSON.stringify(value)}: true`, + ) + .join(', ') +} diff --git a/packages/compiler-vapor/src/generators/dom.ts b/packages/compiler-vapor/src/generators/dom.ts new file mode 100644 index 000000000..b6c2440ff --- /dev/null +++ b/packages/compiler-vapor/src/generators/dom.ts @@ -0,0 +1,43 @@ +import type { CodegenContext } from '../generate' +import type { + AppendNodeIRNode, + InsertNodeIRNode, + PrependNodeIRNode, +} from '../ir' + +export function genInsertNode(oper: InsertNodeIRNode, context: CodegenContext) { + const { newline, pushFnCall, vaporHelper } = context + const elements = ([] as number[]).concat(oper.element) + let element = elements.map((el) => `n${el}`).join(', ') + if (elements.length > 1) element = `[${element}]` + newline() + pushFnCall( + vaporHelper('insert'), + element, + `n${oper.parent}`, + `n${oper.anchor}`, + ) +} + +export function genPrependNode( + oper: PrependNodeIRNode, + context: CodegenContext, +) { + const { newline, pushFnCall, vaporHelper } = context + newline() + pushFnCall( + vaporHelper('prepend'), + `n${oper.parent}`, + oper.elements.map((el) => `n${el}`).join(', '), + ) +} + +export function genAppendNode(oper: AppendNodeIRNode, context: CodegenContext) { + const { newline, pushFnCall, vaporHelper } = context + newline() + pushFnCall( + vaporHelper('append'), + `n${oper.parent}`, + oper.elements.map((el) => `n${el}`).join(', '), + ) +} diff --git a/packages/compiler-vapor/src/generators/event.ts b/packages/compiler-vapor/src/generators/event.ts new file mode 100644 index 000000000..e45fa5889 --- /dev/null +++ b/packages/compiler-vapor/src/generators/event.ts @@ -0,0 +1,87 @@ +import { isMemberExpression } from '@vue/compiler-dom' +import type { CodegenContext } from '../generate' +import type { SetEventIRNode } from '../ir' +import { genExpression } from './expression' + +// TODO: share this with compiler-core +const fnExpRE = + /^\s*([\w$_]+|(async\s*)?\([^)]*?\))\s*(:[^=]+)?=>|^\s*(async\s+)?function(?:\s+[\w$]+)?\s*\(/ + +export function genSetEvent(oper: SetEventIRNode, context: CodegenContext) { + const { vaporHelper, push, newline, pushMulti, pushFnCall } = context + const { keys, nonKeys, options } = oper.modifiers + + newline() + pushFnCall( + vaporHelper('on'), + // 1st arg: event name + () => push(`n${oper.element}`), + // 2nd arg: event name + () => { + if (oper.keyOverride) { + const find = JSON.stringify(oper.keyOverride[0]) + const replacement = JSON.stringify(oper.keyOverride[1]) + pushMulti(['(', ')'], () => genExpression(oper.key, context)) + push(` === ${find} ? ${replacement} : `) + pushMulti(['(', ')'], () => genExpression(oper.key, context)) + } else { + genExpression(oper.key, context) + } + }, + // 3rd arg: event handler + () => { + const pushWithKeys = (fn: () => void) => { + push(`${vaporHelper('withKeys')}(`) + fn() + push(`, ${genArrayExpression(keys)})`) + } + const pushWithModifiers = (fn: () => void) => { + push(`${vaporHelper('withModifiers')}(`) + fn() + push(`, ${genArrayExpression(nonKeys)})`) + } + const pushNoop = (fn: () => void) => fn() + + ;(keys.length ? pushWithKeys : pushNoop)(() => + (nonKeys.length ? pushWithModifiers : pushNoop)(() => { + genEventHandler(context) + }), + ) + }, + // 4th arg, gen options + !!options.length && + (() => push(`{ ${options.map((v) => `${v}: true`).join(', ')} }`)), + ) + + function genEventHandler(context: CodegenContext) { + const exp = oper.value + if (exp && exp.content.trim()) { + const isMemberExp = isMemberExpression(exp.content, context) + const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content)) + const hasMultipleStatements = exp.content.includes(`;`) + + if (isInlineStatement) { + push('$event => ') + push(hasMultipleStatements ? '{' : '(') + const knownIds = Object.create(null) + knownIds['$event'] = 1 + genExpression(exp, context, knownIds) + push(hasMultipleStatements ? '}' : ')') + } else if (isMemberExp) { + push('(...args) => (') + genExpression(exp, context) + push(' && ') + genExpression(exp, context) + push('(...args))') + } else { + genExpression(exp, context) + } + } else { + push('() => {}') + } + } +} + +function genArrayExpression(elements: string[]) { + return `[${elements.map((it) => JSON.stringify(it)).join(', ')}]` +} diff --git a/packages/compiler-vapor/src/generators/expression.ts b/packages/compiler-vapor/src/generators/expression.ts new file mode 100644 index 000000000..d6ae21f51 --- /dev/null +++ b/packages/compiler-vapor/src/generators/expression.ts @@ -0,0 +1,102 @@ +import { + BindingTypes, + NewlineType, + type SourceLocation, + advancePositionWithClone, + walkIdentifiers, +} from '@vue/compiler-dom' +import { isGloballyAllowed, isString, makeMap } from '@vue/shared' +import type { Identifier } from '@babel/types' +import type { IRExpression } from '../ir' +import type { CodegenContext } from '../generate' + +export function genExpression( + node: IRExpression, + context: CodegenContext, + knownIds: Record = Object.create(null), +): void { + const { push } = context + if (isString(node)) return push(node) + + const { content: rawExpr, ast, isStatic, loc } = node + if (isStatic) { + return push(JSON.stringify(rawExpr), NewlineType.None, loc) + } + if ( + __BROWSER__ || + !context.prefixIdentifiers || + !node.content.trim() || + // there was a parsing error + ast === false || + isGloballyAllowed(rawExpr) || + isLiteralWhitelisted(rawExpr) + ) { + return push(rawExpr, NewlineType.None, loc) + } + + if (ast === null) { + // the expression is a simple identifier + return genIdentifier(rawExpr, context, loc) + } + + const ids: Identifier[] = [] + walkIdentifiers( + ast!, + (id, parent, parentStack, isReference, isLocal) => { + if (isLocal) return + ids.push(id) + }, + false, + [], + knownIds, + ) + if (ids.length) { + ids.sort((a, b) => a.start! - b.start!) + ids.forEach((id, i) => { + // range is offset by -1 due to the wrapping parens when parsed + const start = id.start! - 1 + const end = id.end! - 1 + const last = ids[i - 1] + + const leadingText = rawExpr.slice(last ? last.end! - 1 : 0, start) + if (leadingText.length) push(leadingText, NewlineType.Unknown) + + const source = rawExpr.slice(start, end) + genIdentifier(source, context, { + start: advancePositionWithClone(node.loc.start, source, start), + end: advancePositionWithClone(node.loc.start, source, end), + source, + }) + + if (i === ids.length - 1 && end < rawExpr.length) { + push(rawExpr.slice(end), NewlineType.Unknown) + } + }) + } else { + push(rawExpr, NewlineType.Unknown) + } +} + +const isLiteralWhitelisted = /*#__PURE__*/ makeMap('true,false,null,this') + +function genIdentifier( + id: string, + { inline, bindingMetadata, vaporHelper, push }: CodegenContext, + loc?: SourceLocation, +): void { + let name: string | undefined = id + if (inline) { + switch (bindingMetadata[id]) { + case BindingTypes.SETUP_REF: + name = id += '.value' + break + case BindingTypes.SETUP_MAYBE_REF: + id = `${vaporHelper('unref')}(${id})` + name = undefined + break + } + } else { + id = `_ctx.${id}` + } + push(id, NewlineType.None, loc, name) +} diff --git a/packages/compiler-vapor/src/generators/html.ts b/packages/compiler-vapor/src/generators/html.ts new file mode 100644 index 000000000..503d56a22 --- /dev/null +++ b/packages/compiler-vapor/src/generators/html.ts @@ -0,0 +1,11 @@ +import type { CodegenContext } from '../generate' +import type { SetHtmlIRNode } from '../ir' +import { genExpression } from './expression' + +export function genSetHtml(oper: SetHtmlIRNode, context: CodegenContext) { + const { newline, pushFnCall, vaporHelper } = context + newline() + pushFnCall(vaporHelper('setHtml'), `n${oper.element}`, 'undefined', () => + genExpression(oper.value, context), + ) +} diff --git a/packages/compiler-vapor/src/generators/modelValue.ts b/packages/compiler-vapor/src/generators/modelValue.ts new file mode 100644 index 000000000..1cc76246d --- /dev/null +++ b/packages/compiler-vapor/src/generators/modelValue.ts @@ -0,0 +1,35 @@ +import { camelize, isString } from '@vue/shared' +import { genExpression } from './expression' +import type { SetModelValueIRNode } from '../ir' +import type { CodegenContext } from '../generate' + +export function genSetModelValue( + oper: SetModelValueIRNode, + context: CodegenContext, +) { + const { vaporHelper, push, newline, pushFnCall } = context + + newline() + pushFnCall( + vaporHelper('on'), + // 1st arg: event name + () => push(`n${oper.element}`), + // 2nd arg: event name + () => { + if (isString(oper.key)) { + push(JSON.stringify(`update:${camelize(oper.key)}`)) + } else { + push('`update:${') + genExpression(oper.key, context) + push('}`') + } + }, + // 3rd arg: event handler + () => { + push((context.isTS ? `($event: any)` : `$event`) + ' => ((') + // TODO handle not a ref + genExpression(oper.value, context) + push(') = $event)') + }, + ) +} diff --git a/packages/compiler-vapor/src/generators/prop.ts b/packages/compiler-vapor/src/generators/prop.ts new file mode 100644 index 000000000..89e0fc6df --- /dev/null +++ b/packages/compiler-vapor/src/generators/prop.ts @@ -0,0 +1,27 @@ +import type { CodegenContext } from '../generate' +import type { SetPropIRNode } from '../ir' +import { genExpression } from './expression' + +export function genSetProp(oper: SetPropIRNode, context: CodegenContext) { + const { pushFnCall, pushMulti, newline, vaporHelper, helper } = context + + newline() + pushFnCall( + vaporHelper('setDynamicProp'), + `n${oper.element}`, + // 2. key name + () => { + if (oper.runtimeCamelize) { + pushFnCall(helper('camelize'), () => genExpression(oper.key, context)) + } else if (oper.runtimePrefix) { + pushMulti([`\`${oper.runtimePrefix}\${`, `}\``], () => + genExpression(oper.key, context), + ) + } else { + genExpression(oper.key, context) + } + }, + 'undefined', + () => genExpression(oper.value, context), + ) +} diff --git a/packages/compiler-vapor/src/generators/ref.ts b/packages/compiler-vapor/src/generators/ref.ts new file mode 100644 index 000000000..78b8024d7 --- /dev/null +++ b/packages/compiler-vapor/src/generators/ref.ts @@ -0,0 +1,11 @@ +import type { CodegenContext } from '../generate' +import type { SetRefIRNode } from '../ir' +import { genExpression } from './expression' + +export function genSetRef(oper: SetRefIRNode, context: CodegenContext) { + const { newline, pushFnCall, vaporHelper } = context + newline() + pushFnCall(vaporHelper('setRef'), `n${oper.element}`, () => + genExpression(oper.value, context), + ) +} diff --git a/packages/compiler-vapor/src/generators/text.ts b/packages/compiler-vapor/src/generators/text.ts new file mode 100644 index 000000000..c1d708aa6 --- /dev/null +++ b/packages/compiler-vapor/src/generators/text.ts @@ -0,0 +1,22 @@ +import type { CodegenContext } from '../generate' +import type { CreateTextNodeIRNode, SetTextIRNode } from '../ir' +import { genExpression } from './expression' + +export function genSetText(oper: SetTextIRNode, context: CodegenContext) { + const { pushFnCall, newline, vaporHelper } = context + newline() + pushFnCall(vaporHelper('setText'), `n${oper.element}`, 'undefined', () => + genExpression(oper.value, context), + ) +} + +export function genCreateTextNode( + oper: CreateTextNodeIRNode, + context: CodegenContext, +) { + const { pushNewline, pushFnCall, vaporHelper } = context + pushNewline(`const n${oper.id} = `) + pushFnCall(vaporHelper('createTextNode'), () => + genExpression(oper.value, context), + ) +} diff --git a/packages/compiler-vapor/src/ir.ts b/packages/compiler-vapor/src/ir.ts index dbf381dcb..00fcdb587 100644 --- a/packages/compiler-vapor/src/ir.ts +++ b/packages/compiler-vapor/src/ir.ts @@ -1,4 +1,5 @@ import type { + BindingTypes, CompoundExpressionNode, DirectiveNode, RootNode, @@ -106,6 +107,7 @@ export interface SetModelValueIRNode extends BaseIRNode { element: number key: IRExpression value: IRExpression + bindingType?: BindingTypes isComponent: boolean } diff --git a/packages/compiler-vapor/src/transforms/vModel.ts b/packages/compiler-vapor/src/transforms/vModel.ts index adc344ff5..d91a9e090 100644 --- a/packages/compiler-vapor/src/transforms/vModel.ts +++ b/packages/compiler-vapor/src/transforms/vModel.ts @@ -27,7 +27,6 @@ export const transformVModel: DirectiveTransform = (dir, node, context) => { // we assume v-model directives are always parsed // (not artificially created by a transform) const rawExp = exp.loc.source - const expString = exp.content // in SFC