|
|
@@ -21,8 +21,9 @@ export function genTemplates(
|
|
|
export function genChildren(
|
|
|
dynamic: IRDynamicInfo,
|
|
|
context: CodegenContext,
|
|
|
- from: number,
|
|
|
- paths: number[] = [],
|
|
|
+ from: string,
|
|
|
+ path: number[] = [],
|
|
|
+ knownPaths: [id: string, path: number[]][] = [],
|
|
|
): CodeFragment[] {
|
|
|
const { helper } = context
|
|
|
const [frag, push] = buildCodeFragment()
|
|
|
@@ -34,7 +35,7 @@ export function genChildren(
|
|
|
push(...genDirectivesForElement(id, context))
|
|
|
}
|
|
|
|
|
|
- let prev: [id: number, elementIndex: number] | undefined
|
|
|
+ let prev: [variable: string, elementIndex: number] | undefined
|
|
|
for (const [index, child] of children.entries()) {
|
|
|
if (child.flags & DynamicFlag.NON_TEMPLATE) {
|
|
|
offset--
|
|
|
@@ -47,34 +48,81 @@ export function genChildren(
|
|
|
: child.id
|
|
|
: undefined
|
|
|
|
|
|
- const elementIndex = Number(index) + offset
|
|
|
- const newPaths = [...paths, elementIndex]
|
|
|
-
|
|
|
- if (id === undefined) {
|
|
|
- push(...genChildren(child, context, from, newPaths))
|
|
|
+ if (id === undefined && !child.hasDynamicChild) {
|
|
|
+ const { id, template } = child
|
|
|
+ if (id !== undefined && template !== undefined) {
|
|
|
+ push(NEWLINE, `const n${id} = t${template}()`)
|
|
|
+ push(...genDirectivesForElement(id, context))
|
|
|
+ }
|
|
|
continue
|
|
|
}
|
|
|
|
|
|
- push(NEWLINE, `const n${id} = `)
|
|
|
+ const elementIndex = Number(index) + offset
|
|
|
+ const newPath = [...path, elementIndex]
|
|
|
+
|
|
|
+ const variable = id === undefined ? `_n${context.block.tempId++}` : `n${id}`
|
|
|
+ push(NEWLINE, `const ${variable} = `)
|
|
|
+
|
|
|
if (prev) {
|
|
|
const offset = elementIndex - prev[1]
|
|
|
if (offset === 1) {
|
|
|
- push(`n${prev[0]}.nextSibling`)
|
|
|
+ push(...genCall(helper('next'), prev[0]))
|
|
|
} else {
|
|
|
- push(...genCall(helper('next'), `n${prev[0]}`, String(offset)))
|
|
|
+ push(...genCall(helper('nextn'), prev[0], String(offset)))
|
|
|
}
|
|
|
} else {
|
|
|
- if (newPaths.length === 1 && newPaths[0] === 0) {
|
|
|
- push(`n${from}.firstChild`)
|
|
|
+ if (newPath.length === 1 && newPath[0] === 0) {
|
|
|
+ push(...genCall(helper('child'), from))
|
|
|
} else {
|
|
|
- push(
|
|
|
- ...genCall(helper('children'), `n${from}`, ...newPaths.map(String)),
|
|
|
- )
|
|
|
+ // check if there's a node that we can reuse from
|
|
|
+ let resolvedFrom = from
|
|
|
+ let resolvedPath = newPath
|
|
|
+ let skipFirstChild = false
|
|
|
+ outer: for (const [from, path] of knownPaths) {
|
|
|
+ const l = path.length
|
|
|
+ const tail = newPath.slice(l)
|
|
|
+ for (let i = 0; i < l; i++) {
|
|
|
+ const parentSeg = path[i]
|
|
|
+ const thisSeg = newPath[i]
|
|
|
+ if (parentSeg !== thisSeg) {
|
|
|
+ if (i === l - 1) {
|
|
|
+ // last bit is reusable
|
|
|
+ resolvedFrom = from
|
|
|
+ resolvedPath = [thisSeg - parentSeg, ...tail]
|
|
|
+ skipFirstChild = true
|
|
|
+ break outer
|
|
|
+ }
|
|
|
+ break
|
|
|
+ } else if (i === l - 1) {
|
|
|
+ // full overlap
|
|
|
+ resolvedFrom = from
|
|
|
+ resolvedPath = tail
|
|
|
+ break outer
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ let init
|
|
|
+ for (const i of resolvedPath) {
|
|
|
+ init = init
|
|
|
+ ? genCall(helper('child'), init)
|
|
|
+ : skipFirstChild
|
|
|
+ ? resolvedFrom
|
|
|
+ : genCall(helper('child'), resolvedFrom)
|
|
|
+ if (i === 1) {
|
|
|
+ init = genCall(helper('next'), init)
|
|
|
+ } else if (i > 1) {
|
|
|
+ init = genCall(helper('nextn'), init, String(i))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ push(...init!)
|
|
|
}
|
|
|
}
|
|
|
- push(...genDirectivesForElement(id, context))
|
|
|
- prev = [id, elementIndex]
|
|
|
- push(...genChildren(child, context, id, []))
|
|
|
+ if (id !== undefined) {
|
|
|
+ push(...genDirectivesForElement(id, context))
|
|
|
+ }
|
|
|
+ knownPaths.unshift([variable, newPath])
|
|
|
+ prev = [variable, elementIndex]
|
|
|
+ push(...genChildren(child, context, variable))
|
|
|
}
|
|
|
|
|
|
return frag
|