Skip to content

Commit

Permalink
feat: add v-model & v-slot
Browse files Browse the repository at this point in the history
  • Loading branch information
zhiyuanzmj committed Jul 5, 2024
1 parent 69b325c commit 60237d9
Show file tree
Hide file tree
Showing 13 changed files with 620 additions and 8 deletions.
18 changes: 15 additions & 3 deletions playground/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import Count2 from './Count.vue'
import If from './if.vue'
import For from './for.vue'
import Slot from './slot.vue'
import Model from './model.vue'
import Show from './show.vue'
export default defineComponent({
setup() {
Expand Down Expand Up @@ -33,17 +35,27 @@ export default defineComponent({
<fieldset>
<legend>v-if</legend>
<If></If>
<If />
</fieldset>
<fieldset>
<legend>v-for</legend>
<For></For>
<For />
</fieldset>
<fieldset>
<legend>v-slot</legend>
<Slot></Slot>
<Slot />
</fieldset>
<fieldset>
<legend>v-model</legend>
<Model />
</fieldset>
<fieldset>
<legend>v-show</legend>
<Show />
</fieldset>
</>
)
Expand Down
19 changes: 19 additions & 0 deletions playground/model.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script setup lang="tsx">
import { ref } from 'vue'
let model = ref('model')
const Comp = (props) => {
console.log(props)
return <input value={props.modelValue} onInput={e=>props['onUpdate:modelValue'](e.target.value)} />
}
const Render = (
<>
<input v-model={model.value}></input>
<Comp v-model={model.value} />
{model.value}
</>
)
defineRender(Render)
</script>
14 changes: 14 additions & 0 deletions playground/show.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script setup lang="tsx">
import { ref } from 'vue'
let show = ref(false)
const Render = (
<>
<input v-model={show.value} type="checkbox"/>
<span v-show={show.value}>{show.value}</span>
</>
)
defineRender(Render)
</script>
4 changes: 4 additions & 0 deletions src/core/compiler/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { transformText } from './transforms/transformText'
import { transformVBind } from './transforms/vBind'
import { transformVOn } from './transforms/vOn'
import { transformVSlot } from './transforms/vSlot'
import { transformVModel } from './transforms/vModel'
import { transformVShow } from './transforms/vShow'
import type { JSXElement, JSXFragment, Program } from '@babel/types'

export interface VaporCodegenResult
Expand Down Expand Up @@ -129,6 +131,8 @@ export function getBaseTransformPreset(
{
bind: transformVBind,
on: transformVOn,
model: transformVModel,
show: transformVShow,
},
]
}
2 changes: 2 additions & 0 deletions src/core/compiler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ export { transformChildren } from './transforms/transformChildren'
export { transformVBind } from './transforms/vBind'
export { transformVOn } from './transforms/vOn'
export { transformVSlot } from './transforms/vSlot'
export { transformVModel } from './transforms/vModel'
export { transformVShow } from './transforms/vShow'
21 changes: 18 additions & 3 deletions src/core/compiler/transforms/transformElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ export const isReservedProp = /* #__PURE__ */ makeMap(

const __BROWSER__ = false

const isEventRegex = /^on[A-Z]/
const isDirectiveRegex = /^v-[a-z]/

export const transformElement: NodeTransform = (node, context) => {
return function postTransformElement() {
;({ node } = context)
Expand Down Expand Up @@ -255,9 +258,17 @@ function transformProp(
context: TransformContext<JSXElement>,
): DirectiveTransformResult | void {
if (prop.type === 'JSXSpreadAttribute') return
let name = prop.name.type === 'JSXIdentifier' ? prop.name.name : ''
let name =
prop.name.type === 'JSXIdentifier'
? prop.name.name
: prop.name.type === 'JSXNamespacedName'
? prop.name.namespace.name
: ''

if (!prop.value || prop.value.type === 'StringLiteral') {
if (
!isDirectiveRegex.test(name) &&
(!prop.value || prop.value.type === 'StringLiteral')
) {
if (isReservedProp(name)) return
return {
key: resolveSimpleExpression(name, true, prop.name.loc!),
Expand All @@ -268,7 +279,11 @@ function transformProp(
}
}

name = /^on[A-Z]/.test(name) ? 'on' : 'bind'
name = isEventRegex.test(name)
? 'on'
: isDirectiveRegex.test(name)
? name.slice(2)
: 'bind'
const directiveTransform = context.options.directiveTransforms[name]
if (directiveTransform) {
return directiveTransform(prop, node, context)
Expand Down
11 changes: 11 additions & 0 deletions src/core/compiler/transforms/vModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { transformVModel as _transformVModel } from '@vue-vapor/compiler-vapor'
import { resolveDirectiveNode, resolveNode } from '../utils'
import type { DirectiveTransform } from '../transform'

export const transformVModel: DirectiveTransform = (dir, node, context) => {
return _transformVModel(
resolveDirectiveNode(dir, context),
resolveNode(node, context),
context as any,
)
}
11 changes: 11 additions & 0 deletions src/core/compiler/transforms/vShow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { transformVShow as _transformVShow } from '@vue-vapor/compiler-vapor'
import { resolveDirectiveNode, resolveNode } from '../utils'
import type { DirectiveTransform } from '../transform'

export const transformVShow: DirectiveTransform = (dir, node, context) => {
return _transformVShow(
resolveDirectiveNode(dir, context),
resolveNode(node, context),
context as any,
)
}
2 changes: 1 addition & 1 deletion src/core/compiler/transforms/vSlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
type SlotBlockIRNode,
} from '../ir'
import { isComponentNode, resolveExpression } from '../utils'
import { newBlock } from './utils'
import { newBlock } from './utils'
import type { JSXAttribute, JSXElement } from '@babel/types'
import type { NodeTransform, TransformContext } from '../transform'

Expand Down
100 changes: 99 additions & 1 deletion src/core/compiler/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { isGloballyAllowed, isString } from '@vue-vapor/shared'
import {
type AttributeNode,
type DirectiveNode,
type ElementNode,
ElementTypes,
Namespaces,
NodeTypes,
type SimpleExpressionNode,
type TextNode,
findDir as _findDir,
findProp as _findProp,
createSimpleExpression,
Expand All @@ -19,6 +23,7 @@ import type {
BigIntLiteral,
CallExpression,
Expression,
JSXAttribute,
JSXElement,
Node,
NumericLiteral,
Expand Down Expand Up @@ -81,7 +86,7 @@ export function getLiteralExpressionValue(
export function resolveExpression(
node: Node | undefined | null,
context: TransformContext,
) {
): SimpleExpressionNode {
const isStatic =
!!node &&
(node.type === 'StringLiteral' ||
Expand Down Expand Up @@ -154,6 +159,99 @@ export function resolveLocation(
}
}

export function resolveValue(
value: JSXAttribute['value'],
context: TransformContext,
): TextNode | undefined {
return value
? {
type: NodeTypes.TEXT,
content:
value.type === 'StringLiteral'
? value.value
: value.type === 'JSXExpressionContainer'
? context.ir.source.slice(
value.expression.start!,
value.expression.end!,
)
: '',
loc: resolveLocation(value.loc, context),
}
: undefined
}

export function resolveNode(
node: JSXElement,
context: TransformContext,
): ElementNode {
const tag =
node.openingElement.name.type === 'JSXIdentifier'
? node.openingElement.name.name
: ''
const loc = resolveLocation(node.loc, context)
const tagType = isComponentNode(node)
? ElementTypes.COMPONENT
: ElementTypes.ELEMENT
const props = node.openingElement.attributes.reduce(
(result, attr) => {
if (attr.type === 'JSXAttribute') {
if (tagType === ElementTypes.COMPONENT) {
result.push(resolveDirectiveNode(attr, context))
} else {
result.push({
type: NodeTypes.ATTRIBUTE,
name: `${attr.name.name}`,
nameLoc: resolveLocation(attr.name.loc, context),
value: resolveValue(attr.value, context),
loc: resolveLocation(attr.loc, context),
})
}
}
return result
},
[] as Array<AttributeNode | DirectiveNode>,
)

return {
type: NodeTypes.ELEMENT,
props,
children: [],
tag,
loc,
ns: Namespaces.HTML,
tagType,
isSelfClosing: !!node.selfClosing,
codegenNode: undefined,
}
}

export function resolveDirectiveNode(
node: JSXAttribute,
context: TransformContext,
): VaporDirectiveNode {
const { value, name } = node
const nameString = name.type === 'JSXIdentifier' ? name.name : ''
const argString = name.type === 'JSXNamespacedName' ? name.namespace.name : ''

const arg =
name.type === 'JSXNamespacedName'
? resolveSimpleExpression(argString, true, name.namespace.loc)
: undefined
const exp = value ? resolveExpression(value, context) : undefined

const [tag, ...modifiers] = argString.split('_')

return {
type: NodeTypes.DIRECTIVE,
name: nameString,
rawName: `${name}:${tag}`,
exp,
arg,
loc: resolveLocation(node.loc, context),
modifiers,
}
}

export function isComponentNode(node: Node): node is JSXElement {
if (node.type !== 'JSXElement') return false

Expand Down
12 changes: 12 additions & 0 deletions test/transforms/__snapshots__/vShow.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`compiler: v-show transform > simple expression 1`] = `
"import { vShow as _vShow, withDirectives as _withDirectives, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>")
export function render(_ctx) {
const n0 = t0()
_withDirectives(n0, [[_vShow, () => "foo"]])
return n0
}"
`;
Loading

0 comments on commit 60237d9

Please sign in to comment.