Skip to content

wip: template component support #83

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 169 additions & 0 deletions packages/compiler-vapor/src/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
type CodegenOptions,
type CodegenResult,
NewlineType,
NodeTypes,
type Position,
type SourceLocation,
advancePositionWithClone,
Expand All @@ -14,6 +15,7 @@ import {
} from '@vue/compiler-dom'
import {
type AppendNodeIRNode,
type CreateComponentIRNode,
type CreateTextNodeIRNode,
type IRDynamicChildren,
type IRExpression,
Expand Down Expand Up @@ -376,6 +378,8 @@ function genOperation(oper: OperationNode, context: CodegenContext) {
return genSetHtml(oper, context)
case IRNodeTypes.CREATE_TEXT_NODE:
return genCreateTextNode(oper, context)
case IRNodeTypes.CREATE_COMPONENT:
return genCreateComponent(oper, context)
case IRNodeTypes.INSERT_NODE:
return genInsertNode(oper, context)
case IRNodeTypes.PREPEND_NODE:
Expand Down Expand Up @@ -437,6 +441,171 @@ function genCreateTextNode(
)
}

function genCreateComponent(
oper: CreateComponentIRNode,
context: CodegenContext,
) {
const { pushNewline, pushFnCall, vaporHelper } = context

pushNewline(`const n${oper.id} = `)

let props = null
if (oper.props.length && false) {
console.log('ZXZXX')
// props = oper.props
// .map((prop) => {
// const { name, value, exp, arg } = prop
// console.log('using ', prop)

// // const key = oper.runtimeCamelize
// let key = null

// // const content =

// let content = null

// switch (prop?.type) {
// case NodeTypes.ATTRIBUTE: {
// content = value.content
// key = camelize(name)
// break
// }
// case NodeTypes.DIRECTIVE: {
// console.log('found ', prop)
// // content = genExpression(prop.exp, context)
// key = camelize(arg.content)
// content = genWithDirective(exp, context)
// break
// }
// }

// return `${key}: ${JSON.stringify(value?.content)}`
// })
// .join(', ')

console.log('props', props)

// console.log('ppp', oper.props)

// genWithDirective(oper, ctx)

// console.log('res ', genWithDirective({ dir: oper.props[1] }, context))

const dir = oper.props[1]
const { push, newline, pushFnCall, pushMulti, vaporHelper } = context

// genExpression(dir.exp, 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(' }')
}

/*
pushFnCall(
vaporHelper('setAttr'),
`n${oper.element}`,
// 2. key name
() => {
if (oper.runtimeCamelize) {
pushFnCall(helper('camelize'), () => genExpression(oper.key, context))
} else {
genExpression(oper.key, context)
}
},
'undefined',
() => genExpression(oper.value, context),
) */
}
if (oper.props.length) {
props = oper.props
.map((prop) => {
const { name, value, exp, arg } = prop
// const key = oper.runtimeCamelize
let key = null

// const content =

let content = null

switch (prop?.type) {
case NodeTypes.ATTRIBUTE: {
content = value ? JSON.stringify(value.content) : undefined
key = camelize(name)
break
}
case NodeTypes.DIRECTIVE: {
console.log('found ', prop)
// content = genExpression(prop.exp, context)
key = camelize(arg.content)

// content = genWithDirective({ dir: prop }, context)
// genExpression(dir.exp, context)

genIdentifier(
exp.content,
{
inline: context.inline,
bindingMetadata: context.bindingMetadata,
push: (c) => (content = c),
},
{
start: advancePositionWithClone(
exp.loc.start,
exp.content,
exp.loc.start,
),
end: advancePositionWithClone(
exp.loc.start,
exp.content,
exp.loc.end,
),
source: exp.content,
},
)

return `get ${key}() { return ${content} }`

// genExpression(exp, {
// ...context,
// push: (c) => (content = c),
// })
break
}
}

return `${key}: ${content}`
})
.join(', ')

console.log('props', props)
}

pushFnCall(
vaporHelper('createComponent'),
() => genExpression(oper.tag, context),
// () => {
// // oper.props.map((x) => genExpression(x, context))
// },
props ? `{ ${props} }` : '{}',
)
}

function genInsertNode(oper: InsertNodeIRNode, context: CodegenContext) {
const { newline, pushFnCall, vaporHelper } = context
const elements = ([] as number[]).concat(oper.element)
Expand Down
13 changes: 13 additions & 0 deletions packages/compiler-vapor/src/ir.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {
AttributeNode,
CompoundExpressionNode,
DirectiveNode,
RootNode,
Expand All @@ -22,6 +23,7 @@ export enum IRNodeTypes {
PREPEND_NODE,
APPEND_NODE,
CREATE_TEXT_NODE,
CREATE_COMPONENT,

WITH_DIRECTIVE,
}
Expand Down Expand Up @@ -98,6 +100,16 @@ export interface CreateTextNodeIRNode extends BaseIRNode {
value: IRExpression
}

export interface CreateComponentIRNode extends BaseIRNode {
type: IRNodeTypes.CREATE_COMPONENT
id: number
tag: string

children: string[]
props: AttributeNode[]
// children: IRExpression
}

export interface InsertNodeIRNode extends BaseIRNode {
type: IRNodeTypes.INSERT_NODE
element: number | number[]
Expand Down Expand Up @@ -134,6 +146,7 @@ export type OperationNode =
| SetEventIRNode
| SetHtmlIRNode
| CreateTextNodeIRNode
| CreateComponentIRNode
| InsertNodeIRNode
| PrependNodeIRNode
| AppendNodeIRNode
Expand Down
42 changes: 29 additions & 13 deletions packages/compiler-vapor/src/transforms/transformElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,35 @@ export const transformElement: NodeTransform = (node, ctx) => {
const { tag, props } = node
const isComponent = node.tagType === ElementTypes.COMPONENT

ctx.template += `<${tag}`
if (props.length) {
buildProps(
node,
ctx as TransformContext<ElementNode>,
undefined,
isComponent,
)
}
ctx.template += `>` + ctx.childrenTemplate.join('')
if (isComponent) {
ctx.dynamic.ghost = true

ctx.registerOperation({
type: IRNodeTypes.CREATE_COMPONENT,
id: ctx.reference(),
loc: node.loc,
tag: tag,
children: ctx.childrenTemplate,
// TODO add type
props: props,
})
} else {
ctx.template += `<${tag}`

if (props.length) {
buildProps(
node,
ctx as TransformContext<ElementNode>,
undefined,
isComponent,
)
}
ctx.template += `>` + ctx.childrenTemplate.join('')

// TODO remove unnecessary close tag, e.g. if it's the last element of the template
if (!isVoidTag(tag)) {
ctx.template += `</${tag}>`
// TODO remove unnecessary close tag, e.g. if it's the last element of the template
if (!isVoidTag(tag)) {
ctx.template += `</${tag}>`
}
}
}
}
Expand All @@ -49,6 +64,7 @@ function buildProps(
props: ElementNode['props'] = node.props,
isComponent: boolean,
) {
if (isComponent) console.log('props===== ', props)
for (const prop of props) {
transformProp(prop as VaporDirectiveNode | AttributeNode, node, context)
}
Expand Down
6 changes: 5 additions & 1 deletion packages/runtime-vapor/src/component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { EffectScope, type Ref, ref } from '@vue/reactivity'

import { EMPTY_OBJ } from '@vue/shared'
import type { Block } from './render'
import { type Block, render } from './render'
import type { DirectiveBinding } from './directive'
import {
type ComponentPropsOptions,
Expand Down Expand Up @@ -217,3 +217,7 @@ export const createComponentInstance = (
}
return instance
}

export function createComponent(component: Component, props: Data) {
return render(component, props, null).block
}
7 changes: 6 additions & 1 deletion packages/runtime-vapor/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,9 @@ export * from './directive'
export * from './dom'
export * from './directives/vShow'
export * from './apiLifecycle'
export { getCurrentInstance, type ComponentInternalInstance } from './component'

export {
getCurrentInstance,
type ComponentInternalInstance,
createComponent,
} from './component'
15 changes: 9 additions & 6 deletions packages/runtime-vapor/src/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ export function getIsRendering() {
export function render(
comp: Component,
props: Data,
container: string | ParentNode,
container: string | ParentNode | null,
): ComponentInternalInstance {
const instance = createComponentInstance(comp)
initProps(instance, props)
return mountComponent(instance, (container = normalizeContainer(container)))
}

export function normalizeContainer(container: string | ParentNode): ParentNode {
export function normalizeContainer(
container: string | ParentNode | null,
): ParentNode | null {
return typeof container === 'string'
? // eslint-disable-next-line no-restricted-globals
(document.querySelector(container) as ParentNode)
Expand All @@ -40,7 +42,7 @@ export function normalizeContainer(container: string | ParentNode): ParentNode {

export function mountComponent(
instance: ComponentInternalInstance,
container: ParentNode,
container: ParentNode | null,
) {
instance.container = container

Expand Down Expand Up @@ -76,9 +78,10 @@ export function mountComponent(
bm && invokeArrayFns(bm)
invokeDirectiveHook(instance, 'beforeMount')

insert(block, instance.container)
instance.isMountedRef.value = true

if (container) {
insert(block, instance.container)
instance.isMountedRef.value = true
}
// hook: mounted
invokeDirectiveHook(instance, 'mounted')
m && invokeArrayFns(m)
Expand Down
19 changes: 19 additions & 0 deletions playground/src/component-child.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script setup>
import { computed, getCurrentInstance, watchEffect } from '@vue/runtime-vapor'

const child = 'Hello world'

defineProps({
random: String,
})

const ctx = getCurrentInstance()
const random = computed(() => ctx.props.random)

// watchEffect(() => {
// console.log('child', ctx.props.random)
// })
</script>
<template>
<div>Child : {{ child }} -- {{ random }}</div>
</template>
Loading