refactor(compiler-vapor): use class for transform context

This commit is contained in:
三咲智子 Kevin Deng 2024-04-28 03:03:24 +09:00
parent 9b5d23c3e2
commit 784310d4dc
No known key found for this signature in database
2 changed files with 110 additions and 123 deletions

View File

@ -58,37 +58,125 @@ export type StructuralDirectiveTransform = (
export type TransformOptions = HackOptions<BaseTransformOptions>
export interface TransformContext<T extends AllNode = AllNode> {
node: T
parent: TransformContext<RootNode | ElementNode> | null
export class TransformContext<T extends AllNode = AllNode> {
parent: TransformContext<RootNode | ElementNode> | null = null
root: TransformContext<RootNode>
index: number
block: BlockIRNode
index: number = 0
block: BlockIRNode = this.ir.block
options: Required<
Omit<TransformOptions, 'filename' | keyof CompilerCompatOptions>
>
template: string
childrenTemplate: (string | null)[]
dynamic: IRDynamicInfo
template: string = ''
childrenTemplate: (string | null)[] = []
dynamic: IRDynamicInfo = this.ir.block.dynamic
comment: CommentNode[]
inVOnce: boolean = false
inVFor: number = 0
inVOnce: boolean
inVFor: number
comment: CommentNode[] = []
component: Set<string> = this.ir.component
component: Set<string>
private globalId = 0
constructor(
private ir: RootIRNode,
public node: T,
options: TransformOptions = {},
) {
this.options = extend({}, defaultOptions, options)
this.root = this as TransformContext<RootNode>
}
enterBlock(
ir: TransformContext['block'],
isVFor: boolean = false,
): () => void {
const { block, template, dynamic, childrenTemplate } = this
this.block = ir
this.dynamic = ir.dynamic
this.template = ''
this.childrenTemplate = []
isVFor && this.inVFor++
return () => {
// exit
this.registerTemplate()
this.block = block
this.template = template
this.dynamic = dynamic
this.childrenTemplate = childrenTemplate
isVFor && this.inVFor--
}
}
increaseId = () => this.globalId++
reference() {
if (this.dynamic.id !== undefined) return this.dynamic.id
this.dynamic.flags |= DynamicFlag.REFERENCED
return (this.dynamic.id = this.increaseId())
}
pushTemplate(content: string) {
const existing = this.ir.template.findIndex(
template => template === content,
)
if (existing !== -1) return existing
this.ir.template.push(content)
return this.ir.template.length - 1
}
registerTemplate() {
if (!this.template) return -1
const id = this.pushTemplate(this.template)
return (this.dynamic.template = id)
}
enterBlock(ir: TransformContext['block'], isVFor?: boolean): () => void
reference(): number
increaseId(): number
pushTemplate(template: string): number
registerTemplate(customTemplate?: string): number
registerEffect(
expressions: SimpleExpressionNode[],
...operation: OperationNode[]
): void
registerOperation(...operations: OperationNode[]): void
...operations: OperationNode[]
) {
expressions = expressions.filter(exp => !isConstantExpression(exp))
if (this.inVOnce || expressions.length === 0) {
return this.registerOperation(...operations)
}
const existing = this.block.effect.find(e =>
isSameExpression(e.expressions, expressions),
)
if (existing) {
existing.operations.push(...operations)
} else {
this.block.effect.push({
expressions,
operations,
})
}
function isSameExpression(
a: SimpleExpressionNode[],
b: SimpleExpressionNode[],
) {
if (a.length !== b.length) return false
return a.every((exp, i) => exp.content === b[i].content)
}
}
registerOperation(...node: OperationNode[]) {
this.block.operation.push(...node)
}
create<T extends TemplateChildNode>(
node: T,
index: number,
): TransformContext<T> {
return Object.assign(Object.create(TransformContext.prototype), this, {
node,
parent: this as any,
index,
template: '',
childrenTemplate: [],
dynamic: genDefaultDynamic(),
} satisfies Partial<TransformContext<T>>)
}
}
const defaultOptions = {
@ -115,97 +203,6 @@ const defaultOptions = {
onWarn: defaultOnWarn,
}
// TODO use class for better perf
function createRootContext(
root: RootIRNode,
node: RootNode,
options: TransformOptions = {},
): TransformContext<RootNode> {
let globalId = 0
const context: TransformContext<RootNode> = {
node,
parent: null,
index: 0,
root: null!, // set later
block: root.block,
enterBlock(ir, inVFor = false) {
const { block, template, dynamic, childrenTemplate } = this
this.block = ir
this.dynamic = ir.dynamic
this.template = ''
this.childrenTemplate = []
inVFor && this.inVFor++
return () => {
// exit
this.registerTemplate()
this.block = block
this.template = template
this.dynamic = dynamic
this.childrenTemplate = childrenTemplate
inVFor && this.inVFor--
}
},
options: extend({}, defaultOptions, options),
dynamic: root.block.dynamic,
inVOnce: false,
inVFor: 0,
comment: [],
component: root.component,
increaseId: () => globalId++,
reference() {
if (this.dynamic.id !== undefined) return this.dynamic.id
this.dynamic.flags |= DynamicFlag.REFERENCED
return (this.dynamic.id = this.increaseId())
},
registerEffect(expressions, ...operations) {
expressions = expressions.filter(exp => !isConstantExpression(exp))
if (this.inVOnce || expressions.length === 0) {
return this.registerOperation(...operations)
}
const existing = this.block.effect.find(e =>
isSameExpression(e.expressions, expressions),
)
if (existing) {
existing.operations.push(...operations)
} else {
this.block.effect.push({
expressions,
operations,
})
}
function isSameExpression(
a: SimpleExpressionNode[],
b: SimpleExpressionNode[],
) {
if (a.length !== b.length) return false
return a.every((exp, i) => exp.content === b[i].content)
}
},
template: '',
childrenTemplate: [],
pushTemplate(content) {
const existing = root.template.findIndex(template => template === content)
if (existing !== -1) return existing
root.template.push(content)
return root.template.length - 1
},
registerTemplate() {
if (!this.template) return -1
const id = this.pushTemplate(this.template)
return (this.dynamic.template = id)
},
registerOperation(...node) {
this.block.operation.push(...node)
},
}
context.root = context
return context
}
// AST -> IR
export function transform(
root: RootNode,
@ -229,7 +226,7 @@ export function transform(
},
}
const context = createRootContext(ir, root, options)
const context = new TransformContext(ir, root, options)
transformNode(context)

View File

@ -11,8 +11,6 @@ import {
transformNode,
} from '../transform'
import { DynamicFlag, type IRDynamicInfo, IRNodeTypes } from '../ir'
import { extend } from '@vue/shared'
import { genDefaultDynamic } from './utils'
export const transformChildren: NodeTransform = (node, context) => {
const isFragment =
@ -102,13 +100,5 @@ function createContext<T extends TemplateChildNode>(
parent: TransformContext<RootNode | ElementNode>,
index: number,
): TransformContext<T> {
return extend({}, parent, {
node,
parent,
index,
template: '',
childrenTemplate: [],
dynamic: genDefaultDynamic(),
} satisfies Partial<TransformContext<T>>) satisfies TransformContext<T>
return parent.create(node, index)
}