mirror of https://github.com/vuejs/core.git
feat: append multiple node
This commit is contained in:
parent
a3fb85fd00
commit
07373d41fd
|
@ -8,16 +8,16 @@ export function render() {
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const {
|
const {
|
||||||
0: [
|
0: [
|
||||||
n1,
|
n3,
|
||||||
{
|
{
|
||||||
1: [n3],
|
1: [n2],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
} = children(n0);
|
} = children(n0);
|
||||||
const n2 = createTextNode(count.value);
|
const n1 = createTextNode(count.value);
|
||||||
insert(n2, n1, n3);
|
insert(n1, n3, n2);
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
setText(n2, undefined, count.value);
|
setText(n1, undefined, count.value);
|
||||||
});
|
});
|
||||||
return n0;
|
return n0;
|
||||||
}
|
}
|
||||||
|
@ -110,22 +110,22 @@ export function render() {
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`comile > directives > v-once > basic 1`] = `
|
exports[`comile > directives > v-once > basic 1`] = `
|
||||||
"import { template, children, createTextNode, insert, setText, setAttr } from 'vue/vapor';
|
"import { template, children, createTextNode, setText, setAttr, insert } from 'vue/vapor';
|
||||||
const t0 = template('<div> <span></span></div>');
|
const t0 = template('<div> <span></span></div>');
|
||||||
export function render() {
|
export function render() {
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const {
|
const {
|
||||||
0: [
|
0: [
|
||||||
n1,
|
n3,
|
||||||
{
|
{
|
||||||
1: [n3],
|
2: [n2],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
} = children(n0);
|
} = children(n0);
|
||||||
const n2 = createTextNode(msg.value);
|
const n1 = createTextNode(msg.value);
|
||||||
insert(n2, n1, 0 /* InsertPosition.FIRST */);
|
setText(n1, undefined, msg.value);
|
||||||
setText(n2, undefined, msg.value);
|
setAttr(n2, 'class', undefined, clz.value);
|
||||||
setAttr(n3, 'class', undefined, clz.value);
|
insert(n1, n3, 0 /* InsertPosition.FIRST */);
|
||||||
return n0;
|
return n0;
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
|
@ -167,14 +167,13 @@ export function render() {
|
||||||
|
|
||||||
exports[`comile > dynamic root 1`] = `
|
exports[`comile > dynamic root 1`] = `
|
||||||
"import { watchEffect } from 'vue';
|
"import { watchEffect } from 'vue';
|
||||||
import { fragment, createTextNode, insert, setText } from 'vue/vapor';
|
import { fragment, createTextNode, append, setText } from 'vue/vapor';
|
||||||
export function render() {
|
export function render() {
|
||||||
const t0 = fragment();
|
const t0 = fragment();
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const n1 = createTextNode(1);
|
const n1 = createTextNode(1);
|
||||||
insert(n1, n0, 0 /* InsertPosition.FIRST */);
|
|
||||||
const n2 = createTextNode(2);
|
const n2 = createTextNode(2);
|
||||||
insert(n2, n0);
|
append(n0, n1, n2);
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
setText(n1, undefined, 1);
|
setText(n1, undefined, 1);
|
||||||
});
|
});
|
||||||
|
@ -203,8 +202,8 @@ const t0 = template('2');
|
||||||
export function render() {
|
export function render() {
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const n1 = createTextNode(1);
|
const n1 = createTextNode(1);
|
||||||
insert(n1, n0, 0 /* InsertPosition.FIRST */);
|
|
||||||
const n2 = createTextNode(3);
|
const n2 = createTextNode(3);
|
||||||
|
insert(n1, n0, 0 /* InsertPosition.FIRST */);
|
||||||
insert(n2, n0);
|
insert(n2, n0);
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
setText(n1, undefined, 1);
|
setText(n1, undefined, 1);
|
||||||
|
|
|
@ -20,19 +20,19 @@ const increment = () => count.value++
|
||||||
|
|
||||||
return (() => {
|
return (() => {
|
||||||
const n0 = t0()
|
const n0 = t0()
|
||||||
const { 1: [n1], 2: [n3], 3: [n5], 4: [n6], 6: [n7],} = children(n0)
|
const { 1: [n2], 2: [n4], 3: [n5], 4: [n6], 6: [n8],} = children(n0)
|
||||||
const n2 = createTextNode(count.value)
|
const n1 = createTextNode(count.value)
|
||||||
insert(n2, n1)
|
insert(n1, n2)
|
||||||
const n4 = createTextNode(double.value)
|
const n3 = createTextNode(double.value)
|
||||||
insert(n4, n3)
|
insert(n3, n4)
|
||||||
const n8 = createTextNode(count.value)
|
const n7 = createTextNode(count.value)
|
||||||
insert(n8, n7)
|
setText(n7, undefined, count.value)
|
||||||
setText(n8, undefined, count.value)
|
insert(n7, n8)
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
setText(n2, undefined, count.value)
|
setText(n1, undefined, count.value)
|
||||||
})
|
})
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
setText(n4, undefined, double.value)
|
setText(n3, undefined, double.value)
|
||||||
})
|
})
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
on(n5, \\"click\\", increment)
|
on(n5, \\"click\\", increment)
|
||||||
|
|
|
@ -33,11 +33,11 @@ export function generate(
|
||||||
})
|
})
|
||||||
|
|
||||||
{
|
{
|
||||||
code += `const n${ir.children.id} = t0()\n`
|
code += `const n${ir.dynamic.id} = t0()\n`
|
||||||
if (Object.keys(ir.children.children).length) {
|
|
||||||
code += `const {${genChildren(ir.children.children)}} = children(n${
|
const children = genChildren(ir.dynamic.children)
|
||||||
ir.children.id
|
if (children) {
|
||||||
})\n`
|
code += `const ${children} = children(n${ir.dynamic.id})\n`
|
||||||
vaporHelpers.add('children')
|
vaporHelpers.add('children')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ export function generate(
|
||||||
}
|
}
|
||||||
// TODO multiple-template
|
// TODO multiple-template
|
||||||
// TODO return statement in IR
|
// TODO return statement in IR
|
||||||
code += `return n${ir.children.id}\n`
|
code += `return n${ir.dynamic.id}\n`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vaporHelpers.size)
|
if (vaporHelpers.size)
|
||||||
|
@ -79,59 +79,65 @@ export function generate(
|
||||||
preamble,
|
preamble,
|
||||||
}
|
}
|
||||||
|
|
||||||
function genOperation(operation: OperationNode) {
|
function genOperation(oper: OperationNode) {
|
||||||
let code = ''
|
let code = ''
|
||||||
|
|
||||||
// TODO: cache old value
|
// TODO: cache old value
|
||||||
switch (operation.type) {
|
switch (oper.type) {
|
||||||
case IRNodeTypes.SET_PROP: {
|
case IRNodeTypes.SET_PROP: {
|
||||||
code = `setAttr(n${operation.element}, ${JSON.stringify(
|
code = `setAttr(n${oper.element}, ${JSON.stringify(
|
||||||
operation.name,
|
oper.name,
|
||||||
)}, undefined, ${operation.value})\n`
|
)}, undefined, ${oper.value})\n`
|
||||||
vaporHelpers.add('setAttr')
|
vaporHelpers.add('setAttr')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
case IRNodeTypes.SET_TEXT: {
|
case IRNodeTypes.SET_TEXT: {
|
||||||
code = `setText(n${operation.element}, undefined, ${operation.value})\n`
|
code = `setText(n${oper.element}, undefined, ${oper.value})\n`
|
||||||
vaporHelpers.add('setText')
|
vaporHelpers.add('setText')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
case IRNodeTypes.SET_EVENT: {
|
case IRNodeTypes.SET_EVENT: {
|
||||||
code = `on(n${operation.element}, ${JSON.stringify(operation.name)}, ${
|
code = `on(n${oper.element}, ${JSON.stringify(oper.name)}, ${
|
||||||
operation.value
|
oper.value
|
||||||
})\n`
|
})\n`
|
||||||
vaporHelpers.add('on')
|
vaporHelpers.add('on')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
case IRNodeTypes.SET_HTML: {
|
case IRNodeTypes.SET_HTML: {
|
||||||
code = `setHtml(n${operation.element}, undefined, ${operation.value})\n`
|
code = `setHtml(n${oper.element}, undefined, ${oper.value})\n`
|
||||||
vaporHelpers.add('setHtml')
|
vaporHelpers.add('setHtml')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
case IRNodeTypes.CREATE_TEXT_NODE: {
|
case IRNodeTypes.CREATE_TEXT_NODE: {
|
||||||
code = `const n${operation.id} = createTextNode(${operation.value})\n`
|
code = `const n${oper.id} = createTextNode(${oper.value})\n`
|
||||||
vaporHelpers.add('createTextNode')
|
vaporHelpers.add('createTextNode')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
case IRNodeTypes.INSERT_NODE: {
|
case IRNodeTypes.INSERT_NODE: {
|
||||||
let anchor = ''
|
let anchor = ''
|
||||||
if (typeof operation.anchor === 'number') {
|
if (typeof oper.anchor === 'number') {
|
||||||
anchor = `, n${operation.anchor}`
|
anchor = `, n${oper.anchor}`
|
||||||
} else if (operation.anchor === 'first') {
|
} else if (oper.anchor === 'first') {
|
||||||
anchor = `, 0 /* InsertPosition.FIRST */`
|
anchor = `, 0 /* InsertPosition.FIRST */`
|
||||||
}
|
}
|
||||||
code = `insert(n${operation.element}, n${operation.parent}${anchor})\n`
|
code = `insert(n${oper.element}, n${oper.parent}${anchor})\n`
|
||||||
vaporHelpers.add('insert')
|
vaporHelpers.add('insert')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case IRNodeTypes.APPEND_NODE: {
|
||||||
|
code = `append(n${oper.parent}, ${oper.elements
|
||||||
|
.map((el) => `n${el}`)
|
||||||
|
.join(', ')})\n`
|
||||||
|
vaporHelpers.add('append')
|
||||||
|
break
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
checkNever(operation)
|
checkNever(oper)
|
||||||
}
|
}
|
||||||
|
|
||||||
return code
|
return code
|
||||||
|
@ -139,16 +145,26 @@ export function generate(
|
||||||
}
|
}
|
||||||
|
|
||||||
function genChildren(children: DynamicChildren) {
|
function genChildren(children: DynamicChildren) {
|
||||||
let str = ''
|
let code = ''
|
||||||
|
// TODO
|
||||||
|
let offset = 0
|
||||||
|
|
||||||
for (const [index, child] of Object.entries(children)) {
|
for (const [index, child] of Object.entries(children)) {
|
||||||
str += ` ${index}: [`
|
const childrenLength = Object.keys(child.children).length
|
||||||
if (child.store) {
|
if (child.ghost && child.placeholder === null && childrenLength === 0)
|
||||||
str += `n${child.id}`
|
continue
|
||||||
}
|
|
||||||
if (Object.keys(child.children).length) {
|
code += ` ${Number(index) + offset}: [`
|
||||||
str += `, {${genChildren(child.children)}}`
|
|
||||||
}
|
const id = child.ghost ? child.placeholder : child.id
|
||||||
str += '],'
|
if (id !== null) code += `n${id}`
|
||||||
|
|
||||||
|
const childrenString = childrenLength && genChildren(child.children)
|
||||||
|
if (childrenString) code += `, ${childrenString}`
|
||||||
|
|
||||||
|
code += '],'
|
||||||
}
|
}
|
||||||
return str
|
|
||||||
|
if (!code) return ''
|
||||||
|
return `{${code}}`
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ export const enum IRNodeTypes {
|
||||||
SET_HTML,
|
SET_HTML,
|
||||||
|
|
||||||
INSERT_NODE,
|
INSERT_NODE,
|
||||||
|
APPEND_NODE,
|
||||||
CREATE_TEXT_NODE,
|
CREATE_TEXT_NODE,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ export interface IRNode {
|
||||||
export interface RootIRNode extends IRNode {
|
export interface RootIRNode extends IRNode {
|
||||||
type: IRNodeTypes.ROOT
|
type: IRNodeTypes.ROOT
|
||||||
template: Array<TemplateFactoryIRNode | FragmentFactoryIRNode>
|
template: Array<TemplateFactoryIRNode | FragmentFactoryIRNode>
|
||||||
children: DynamicChild
|
dynamic: DynamicInfo
|
||||||
// TODO multi-expression effect
|
// TODO multi-expression effect
|
||||||
effect: Record<string /* expr */, OperationNode[]>
|
effect: Record<string /* expr */, OperationNode[]>
|
||||||
operation: OperationNode[]
|
operation: OperationNode[]
|
||||||
|
@ -71,11 +72,18 @@ export interface CreateTextNodeIRNode extends IRNode {
|
||||||
value: string
|
value: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type InsertAnchor = number | 'first' | 'last'
|
||||||
export interface InsertNodeIRNode extends IRNode {
|
export interface InsertNodeIRNode extends IRNode {
|
||||||
type: IRNodeTypes.INSERT_NODE
|
type: IRNodeTypes.INSERT_NODE
|
||||||
element: number
|
element: number
|
||||||
parent: number
|
parent: number
|
||||||
anchor: number | 'first' | 'last'
|
anchor: InsertAnchor
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AppendNodeIRNode extends IRNode {
|
||||||
|
type: IRNodeTypes.APPEND_NODE
|
||||||
|
elements: number[]
|
||||||
|
parent: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OperationNode =
|
export type OperationNode =
|
||||||
|
@ -85,11 +93,14 @@ export type OperationNode =
|
||||||
| SetHtmlIRNode
|
| SetHtmlIRNode
|
||||||
| CreateTextNodeIRNode
|
| CreateTextNodeIRNode
|
||||||
| InsertNodeIRNode
|
| InsertNodeIRNode
|
||||||
|
| AppendNodeIRNode
|
||||||
|
|
||||||
export interface DynamicChild {
|
export interface DynamicInfo {
|
||||||
id: number | null
|
id: number | null
|
||||||
store: boolean
|
referenced: boolean
|
||||||
|
/** created by DOM API */
|
||||||
ghost: boolean
|
ghost: boolean
|
||||||
|
placeholder: number | null
|
||||||
children: DynamicChildren
|
children: DynamicChildren
|
||||||
}
|
}
|
||||||
export type DynamicChildren = Record<number, DynamicChild>
|
export type DynamicChildren = Record<number, DynamicInfo>
|
||||||
|
|
|
@ -11,10 +11,11 @@ import type {
|
||||||
ExpressionNode,
|
ExpressionNode,
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import {
|
import {
|
||||||
type DynamicChildren,
|
|
||||||
type OperationNode,
|
type OperationNode,
|
||||||
type RootIRNode,
|
type RootIRNode,
|
||||||
IRNodeTypes,
|
IRNodeTypes,
|
||||||
|
DynamicInfo,
|
||||||
|
InsertAnchor,
|
||||||
} from './ir'
|
} from './ir'
|
||||||
import { isVoidTag } from '@vue/shared'
|
import { isVoidTag } from '@vue/shared'
|
||||||
|
|
||||||
|
@ -24,14 +25,13 @@ export interface TransformContext<T extends Node = Node> {
|
||||||
root: TransformContext<RootNode>
|
root: TransformContext<RootNode>
|
||||||
index: number
|
index: number
|
||||||
options: TransformOptions
|
options: TransformOptions
|
||||||
template: string
|
|
||||||
children: DynamicChildren
|
|
||||||
store: boolean
|
|
||||||
ghost: boolean
|
|
||||||
once: boolean
|
|
||||||
id: number | null
|
|
||||||
|
|
||||||
getId(): number
|
template: string
|
||||||
|
dynamic: DynamicInfo
|
||||||
|
|
||||||
|
once: boolean
|
||||||
|
|
||||||
|
reference(): number
|
||||||
incraseId(): number
|
incraseId(): number
|
||||||
registerTemplate(): number
|
registerTemplate(): number
|
||||||
registerEffect(expr: string, operation: OperationNode): void
|
registerEffect(expr: string, operation: OperationNode): void
|
||||||
|
@ -53,18 +53,19 @@ function createRootContext(
|
||||||
index: 0,
|
index: 0,
|
||||||
root: undefined as any, // set later
|
root: undefined as any, // set later
|
||||||
options,
|
options,
|
||||||
children: {},
|
dynamic: ir.dynamic,
|
||||||
store: false,
|
|
||||||
ghost: false,
|
|
||||||
once: false,
|
once: false,
|
||||||
|
|
||||||
id: null,
|
|
||||||
incraseId: () => globalId++,
|
incraseId: () => globalId++,
|
||||||
getId() {
|
reference() {
|
||||||
if (this.id !== null) return this.id
|
if (this.dynamic.id !== null) return this.dynamic.id
|
||||||
return (this.id = this.incraseId())
|
this.dynamic.referenced = true
|
||||||
|
return (this.dynamic.id = this.incraseId())
|
||||||
},
|
},
|
||||||
registerEffect(expr, operation) {
|
registerEffect(expr, operation) {
|
||||||
|
if (this.once) {
|
||||||
|
return this.registerOpration(operation)
|
||||||
|
}
|
||||||
if (!effect[expr]) effect[expr] = []
|
if (!effect[expr]) effect[expr] = []
|
||||||
effect[expr].push(operation)
|
effect[expr].push(operation)
|
||||||
},
|
},
|
||||||
|
@ -97,6 +98,7 @@ function createRootContext(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
ctx.root = ctx
|
ctx.root = ctx
|
||||||
|
ctx.reference()
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,27 +107,19 @@ function createContext<T extends TemplateChildNode>(
|
||||||
parent: TransformContext,
|
parent: TransformContext,
|
||||||
index: number,
|
index: number,
|
||||||
): TransformContext<T> {
|
): TransformContext<T> {
|
||||||
const children = {}
|
|
||||||
|
|
||||||
const ctx: TransformContext<T> = {
|
const ctx: TransformContext<T> = {
|
||||||
...parent,
|
...parent,
|
||||||
id: null,
|
|
||||||
node,
|
node,
|
||||||
parent,
|
parent,
|
||||||
index,
|
index,
|
||||||
get template() {
|
|
||||||
return parent.template
|
template: '',
|
||||||
},
|
dynamic: {
|
||||||
set template(t) {
|
id: null,
|
||||||
parent.template = t
|
referenced: false,
|
||||||
},
|
ghost: false,
|
||||||
children,
|
placeholder: null,
|
||||||
store: false,
|
children: {},
|
||||||
registerEffect(expr, operation) {
|
|
||||||
if (ctx.once) {
|
|
||||||
return ctx.registerOpration(operation)
|
|
||||||
}
|
|
||||||
return parent.registerEffect(expr, operation)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return ctx
|
return ctx
|
||||||
|
@ -140,23 +134,22 @@ export function transform(
|
||||||
type: IRNodeTypes.ROOT,
|
type: IRNodeTypes.ROOT,
|
||||||
loc: root.loc,
|
loc: root.loc,
|
||||||
template: [],
|
template: [],
|
||||||
children: {} as any,
|
dynamic: {
|
||||||
|
id: null,
|
||||||
|
referenced: true,
|
||||||
|
ghost: true,
|
||||||
|
placeholder: null,
|
||||||
|
children: {},
|
||||||
|
},
|
||||||
effect: Object.create(null),
|
effect: Object.create(null),
|
||||||
operation: [],
|
operation: [],
|
||||||
helpers: new Set([]),
|
helpers: new Set([]),
|
||||||
vaporHelpers: new Set([]),
|
vaporHelpers: new Set([]),
|
||||||
}
|
}
|
||||||
const ctx = createRootContext(ir, root, options)
|
const ctx = createRootContext(ir, root, options)
|
||||||
const rootId = ctx.getId()
|
|
||||||
|
|
||||||
// TODO: transform presets, see packages/compiler-core/src/transforms
|
// TODO: transform presets, see packages/compiler-core/src/transforms
|
||||||
transformChildren(ctx, true)
|
transformChildren(ctx, true)
|
||||||
ir.children = {
|
|
||||||
id: rootId,
|
|
||||||
store: true,
|
|
||||||
ghost: false,
|
|
||||||
children: ctx.children,
|
|
||||||
}
|
|
||||||
if (ir.template.length === 0) {
|
if (ir.template.length === 0) {
|
||||||
ir.template.push({
|
ir.template.push({
|
||||||
type: IRNodeTypes.FRAGMENT_FACTORY,
|
type: IRNodeTypes.FRAGMENT_FACTORY,
|
||||||
|
@ -174,15 +167,57 @@ function transformChildren(
|
||||||
const {
|
const {
|
||||||
node: { children },
|
node: { children },
|
||||||
} = ctx
|
} = ctx
|
||||||
let index = 0
|
const childrenTemplate: string[] = []
|
||||||
children.forEach((child, i) => walkNode(child, i))
|
children.forEach((child, i) => walkNode(child, i))
|
||||||
|
|
||||||
|
const dynamicChildren = Object.values(ctx.dynamic.children)
|
||||||
|
const dynamicCount = dynamicChildren.reduce(
|
||||||
|
(prev, child) => prev + (child.ghost ? 1 : 0),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if (dynamicCount === children.length) {
|
||||||
|
// all dynamic node
|
||||||
|
ctx.registerOpration({
|
||||||
|
type: IRNodeTypes.APPEND_NODE,
|
||||||
|
loc: ctx.node.loc,
|
||||||
|
elements: dynamicChildren.map((child) => child.id!),
|
||||||
|
parent: ctx.reference(),
|
||||||
|
})
|
||||||
|
} else if (dynamicCount > 0 && dynamicCount < children.length) {
|
||||||
|
// mixed
|
||||||
|
for (const [indexString, child] of Object.entries(ctx.dynamic.children)) {
|
||||||
|
if (!child.ghost) continue
|
||||||
|
|
||||||
|
const index = Number(indexString)
|
||||||
|
let anchor: InsertAnchor
|
||||||
|
if (index === 0) {
|
||||||
|
anchor = 'first'
|
||||||
|
} else if (index === children.length - 1) {
|
||||||
|
anchor = 'last'
|
||||||
|
} else {
|
||||||
|
childrenTemplate[index] = `<!>`
|
||||||
|
anchor = child.placeholder = ctx.incraseId()
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.registerOpration({
|
||||||
|
type: IRNodeTypes.INSERT_NODE,
|
||||||
|
loc: ctx.node.loc,
|
||||||
|
element: child.id!,
|
||||||
|
parent: ctx.reference(),
|
||||||
|
anchor,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.template += childrenTemplate.join('')
|
||||||
|
|
||||||
|
// finalize template
|
||||||
if (root) ctx.registerTemplate()
|
if (root) ctx.registerTemplate()
|
||||||
|
|
||||||
function walkNode(node: TemplateChildNode, i: number) {
|
function walkNode(node: TemplateChildNode, index: number) {
|
||||||
const child = createContext(node, ctx, index)
|
const child = createContext(node, ctx, index)
|
||||||
const isFirst = i === 0
|
const isFirst = index === 0
|
||||||
const isLast = i === children.length - 1
|
const isLast = index === children.length - 1
|
||||||
|
|
||||||
switch (node.type) {
|
switch (node.type) {
|
||||||
case 1 satisfies NodeTypes.ELEMENT: {
|
case 1 satisfies NodeTypes.ELEMENT: {
|
||||||
|
@ -190,11 +225,11 @@ function transformChildren(
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 2 satisfies NodeTypes.TEXT: {
|
case 2 satisfies NodeTypes.TEXT: {
|
||||||
ctx.template += node.content
|
child.template += node.content
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 3 satisfies NodeTypes.COMMENT: {
|
case 3 satisfies NodeTypes.COMMENT: {
|
||||||
ctx.template += `<!--${node.content}-->`
|
child.template += `<!--${node.content}-->`
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 5 satisfies NodeTypes.INTERPOLATION: {
|
case 5 satisfies NodeTypes.INTERPOLATION: {
|
||||||
|
@ -214,19 +249,20 @@ function transformChildren(
|
||||||
// IfNode
|
// IfNode
|
||||||
// IfBranchNode
|
// IfBranchNode
|
||||||
// ForNode
|
// ForNode
|
||||||
ctx.template += `[type: ${node.type}]`
|
child.template += `[type: ${node.type}]`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Object.keys(child.children).length > 0 || child.store)
|
childrenTemplate.push(child.template)
|
||||||
ctx.children[index] = {
|
|
||||||
id: child.store ? child.getId() : null,
|
|
||||||
store: child.store,
|
|
||||||
children: child.children,
|
|
||||||
ghost: child.ghost,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!child.ghost) index++
|
if (
|
||||||
|
child.dynamic.ghost ||
|
||||||
|
child.dynamic.referenced ||
|
||||||
|
child.dynamic.placeholder ||
|
||||||
|
Object.keys(child.dynamic.children).length
|
||||||
|
) {
|
||||||
|
ctx.dynamic.children[index] = child.dynamic
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,62 +290,38 @@ function transformInterpolation(
|
||||||
) {
|
) {
|
||||||
const { node } = ctx
|
const { node } = ctx
|
||||||
|
|
||||||
if (node.content.type === (4 satisfies NodeTypes.SIMPLE_EXPRESSION)) {
|
if (node.content.type === (8 satisfies NodeTypes.COMPOUND_EXPRESSION)) {
|
||||||
const expr = processExpression(ctx, node.content)!
|
// TODO: CompoundExpressionNode: {{ count + 1 }}
|
||||||
|
|
||||||
const parent = ctx.parent!
|
|
||||||
const parentId = parent.getId()
|
|
||||||
parent.store = true
|
|
||||||
|
|
||||||
if (isFirst && isLast) {
|
|
||||||
ctx.registerEffect(expr, {
|
|
||||||
type: IRNodeTypes.SET_TEXT,
|
|
||||||
loc: node.loc,
|
|
||||||
element: parentId,
|
|
||||||
value: expr,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
let id: number
|
|
||||||
let anchor: number | 'first' | 'last'
|
|
||||||
|
|
||||||
if (!isFirst && !isLast) {
|
|
||||||
id = ctx.incraseId()
|
|
||||||
anchor = ctx.getId()
|
|
||||||
ctx.template += '<!>'
|
|
||||||
ctx.store = true
|
|
||||||
} else {
|
|
||||||
id = ctx.getId()
|
|
||||||
ctx.ghost = true
|
|
||||||
anchor = isFirst ? 'first' : 'last'
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.registerOpration(
|
|
||||||
{
|
|
||||||
type: IRNodeTypes.CREATE_TEXT_NODE,
|
|
||||||
loc: node.loc,
|
|
||||||
id,
|
|
||||||
value: expr,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: IRNodeTypes.INSERT_NODE,
|
|
||||||
loc: node.loc,
|
|
||||||
element: id,
|
|
||||||
parent: parentId,
|
|
||||||
anchor,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
ctx.registerEffect(expr, {
|
|
||||||
type: IRNodeTypes.SET_TEXT,
|
|
||||||
loc: node.loc,
|
|
||||||
element: id,
|
|
||||||
value: expr,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: CompoundExpressionNode: {{ count + 1 }}
|
const expr = processExpression(ctx, node.content)!
|
||||||
|
|
||||||
|
if (isFirst && isLast) {
|
||||||
|
const parent = ctx.parent!
|
||||||
|
const parentId = parent.reference()
|
||||||
|
ctx.registerEffect(expr, {
|
||||||
|
type: IRNodeTypes.SET_TEXT,
|
||||||
|
loc: node.loc,
|
||||||
|
element: parentId,
|
||||||
|
value: expr,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const id = ctx.reference()
|
||||||
|
ctx.dynamic.ghost = true
|
||||||
|
ctx.registerOpration({
|
||||||
|
type: IRNodeTypes.CREATE_TEXT_NODE,
|
||||||
|
loc: node.loc,
|
||||||
|
id,
|
||||||
|
value: expr,
|
||||||
|
})
|
||||||
|
ctx.registerEffect(expr, {
|
||||||
|
type: IRNodeTypes.SET_TEXT,
|
||||||
|
loc: node.loc,
|
||||||
|
element: id,
|
||||||
|
value: expr,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function transformProp(
|
function transformProp(
|
||||||
|
@ -327,7 +339,6 @@ function transformProp(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.store = true
|
|
||||||
const expr = processExpression(ctx, node.exp)
|
const expr = processExpression(ctx, node.exp)
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case 'bind': {
|
case 'bind': {
|
||||||
|
@ -348,7 +359,7 @@ function transformProp(
|
||||||
ctx.registerEffect(expr, {
|
ctx.registerEffect(expr, {
|
||||||
type: IRNodeTypes.SET_PROP,
|
type: IRNodeTypes.SET_PROP,
|
||||||
loc: node.loc,
|
loc: node.loc,
|
||||||
element: ctx.getId(),
|
element: ctx.reference(),
|
||||||
name: node.arg.content,
|
name: node.arg.content,
|
||||||
value: expr,
|
value: expr,
|
||||||
})
|
})
|
||||||
|
@ -372,7 +383,7 @@ function transformProp(
|
||||||
ctx.registerEffect(expr, {
|
ctx.registerEffect(expr, {
|
||||||
type: IRNodeTypes.SET_EVENT,
|
type: IRNodeTypes.SET_EVENT,
|
||||||
loc: node.loc,
|
loc: node.loc,
|
||||||
element: ctx.getId(),
|
element: ctx.reference(),
|
||||||
name: node.arg.content,
|
name: node.arg.content,
|
||||||
value: expr,
|
value: expr,
|
||||||
})
|
})
|
||||||
|
@ -383,7 +394,7 @@ function transformProp(
|
||||||
ctx.registerEffect(value, {
|
ctx.registerEffect(value, {
|
||||||
type: IRNodeTypes.SET_HTML,
|
type: IRNodeTypes.SET_HTML,
|
||||||
loc: node.loc,
|
loc: node.loc,
|
||||||
element: ctx.getId(),
|
element: ctx.reference(),
|
||||||
value,
|
value,
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
|
@ -393,7 +404,7 @@ function transformProp(
|
||||||
ctx.registerEffect(value, {
|
ctx.registerEffect(value, {
|
||||||
type: IRNodeTypes.SET_TEXT,
|
type: IRNodeTypes.SET_TEXT,
|
||||||
loc: node.loc,
|
loc: node.loc,
|
||||||
element: ctx.getId(),
|
element: ctx.reference(),
|
||||||
value,
|
value,
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
|
|
|
@ -57,6 +57,10 @@ export function insert(
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function append(parent: ParentNode, ...nodes: (Node | string)[]) {
|
||||||
|
parent.append(...nodes)
|
||||||
|
}
|
||||||
|
|
||||||
export function remove(block: Block, parent: ParentNode) {
|
export function remove(block: Block, parent: ParentNode) {
|
||||||
if (block instanceof Node) {
|
if (block instanceof Node) {
|
||||||
parent.removeChild(block)
|
parent.removeChild(block)
|
||||||
|
@ -124,6 +128,6 @@ export function children(n: ChildNode): Children {
|
||||||
return { ...Array.from(n.childNodes).map(n => [n, children(n)]) }
|
return { ...Array.from(n.childNodes).map(n => [n, children(n)]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createTextNode(data: string): Text {
|
export function createTextNode(val: unknown): Text {
|
||||||
return document.createTextNode(data)
|
return document.createTextNode(toDisplayString(val))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<template>
|
||||||
|
1{{ 2 }}{{ 3 }}4
|
||||||
|
<div>div</div>
|
||||||
|
</template>
|
Loading…
Reference in New Issue