advancePositionWithMutation,
locStub,
BindingTypes,
- isSimpleIdentifier,
createSimpleExpression,
+ walkIdentifiers,
+ advancePositionWithClone,
} from '@vue/compiler-dom'
import {
type IRDynamicChildren,
type SetEventIRNode,
type WithDirectiveIRNode,
type SetTextIRNode,
+ type SetHtmlIRNode,
+ type CreateTextNodeIRNode,
+ type InsertNodeIRNode,
+ type PrependNodeIRNode,
+ type AppendNodeIRNode,
IRNodeTypes,
- SetHtmlIRNode,
- CreateTextNodeIRNode,
- InsertNodeIRNode,
- PrependNodeIRNode,
- AppendNodeIRNode,
} from './ir'
import { SourceMapGenerator } from 'source-map-js'
import { camelize, isString } from '@vue/shared'
+import type { Identifier } from '@babel/types'
// remove when stable
// @ts-expect-error
// TODO resolve directive
const directiveReference = camelize(`v-${oper.name}`)
if (bindingMetadata[directiveReference]) {
- genExpression(createSimpleExpression(directiveReference), context)
+ const directiveExpression = createSimpleExpression(directiveReference)
+ directiveExpression.ast = null
+ genExpression(directiveExpression, context)
}
if (oper.binding) {
return `[${elements.map((it) => JSON.stringify(it)).join(', ')}]`
}
-function genExpression(
- exp: IRExpression,
- {
- inline,
- prefixIdentifiers,
- bindingMetadata,
- vaporHelper,
- push,
- }: CodegenContext,
-) {
- if (isString(exp)) return push(exp)
+function genExpression(node: IRExpression, context: CodegenContext): void {
+ const { push } = context
+ if (isString(node)) return push(node)
+
+ const { content: rawExpr, ast, isStatic, loc } = node
+ if (__BROWSER__) {
+ return push(rawExpr)
+ }
+
+ if (
+ !context.prefixIdentifiers ||
+ !node.content.trim() ||
+ // there was a parsing error
+ ast === false
+ ) {
+ return push(rawExpr, NewlineType.None, node.loc)
+ }
+ if (isStatic) {
+ // TODO
+ return push(JSON.stringify(rawExpr))
+ }
- let { content } = exp
- let name: string | undefined
+ if (ast === null) {
+ // the expression is a simple identifier
+ return genIdentifier(rawExpr, context, loc)
+ }
- if (exp.isStatic) {
- content = JSON.stringify(content)
- } else {
- switch (bindingMetadata[content]) {
+ const ids: Identifier[] = []
+ walkIdentifiers(
+ ast!,
+ (id) => {
+ ids.push(id)
+ },
+ true,
+ )
+ 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)
+ }
+ })
+}
+
+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:
- content += '.value'
+ name = id += '.value'
break
case BindingTypes.SETUP_MAYBE_REF:
- content = `${vaporHelper('unref')}(${content})`
+ id = `${vaporHelper('unref')}(${id})`
+ name = undefined
break
}
- if (prefixIdentifiers && !inline) {
- if (isSimpleIdentifier(content)) name = content
- content = `_ctx.${content}`
- }
+ } else {
+ id = `_ctx.${id}`
}
-
- push(content, NewlineType.None, exp.loc, name)
+ push(id, NewlineType.None, loc, name)
}
type IRExpression,
IRNodeTypes,
} from './ir'
-import type { HackDirectiveNode, HackOptions } from './ir'
+import type { VaporDirectiveNode, HackOptions } from './ir'
export type NodeTransform = (
node: RootNode | TemplateChildNode,
) => void | (() => void) | (() => void)[]
export type DirectiveTransform = (
- dir: HackDirectiveNode,
+ dir: VaporDirectiveNode,
node: ElementNode,
context: TransformContext<ElementNode>,
- // a platform specific compiler can import the base transform and augment
- // it by passing in this optional argument.
- // augmentor?: (ret: DirectiveTransformResult) => DirectiveTransformResult,
) => void
export type TransformOptions = HackOptions<BaseTransformOptions>
} from '@vue/compiler-dom'
import { isBuiltInDirective, isVoidTag } from '@vue/shared'
import { NodeTransform, TransformContext } from '../transform'
-import { HackDirectiveNode, IRNodeTypes } from '../ir'
+import { VaporDirectiveNode, IRNodeTypes } from '../ir'
export const transformElement: NodeTransform = (node, ctx) => {
return function postTransformElement() {
isComponent: boolean,
) {
for (const prop of props) {
- transformProp(prop as HackDirectiveNode | AttributeNode, node, context)
+ transformProp(prop as VaporDirectiveNode | AttributeNode, node, context)
}
}
function transformProp(
- prop: HackDirectiveNode | AttributeNode,
+ prop: VaporDirectiveNode | AttributeNode,
node: ElementNode,
context: TransformContext<ElementNode>,
): void {