mirror of https://github.com/vuejs/core.git
refactor(compiler-vapor): use class for transform context
This commit is contained in:
parent
9b5d23c3e2
commit
784310d4dc
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue