feat(compiler-vapor): resolve directive

This commit is contained in:
三咲智子 Kevin Deng 2024-05-13 02:08:58 +08:00
parent 30f98942db
commit e2b51d6e7a
No known key found for this signature in database
11 changed files with 143 additions and 115 deletions

View File

@ -16,30 +16,34 @@ exports[`compile > custom directive > basic 1`] = `
const t0 = _template("<div></div>")
export function render(_ctx) {
const _directive_test = _resolveDirective("test")
const _directive_hello = _resolveDirective("hello")
const n0 = t0()
_withDirectives(n0, [[_resolveDirective("vTest")], [_resolveDirective("vHello"), void 0, void 0, { world: true }]])
_withDirectives(n0, [[_directive_test], [_directive_hello, void 0, void 0, { world: true }]])
return n0
}"
`;
exports[`compile > custom directive > component 1`] = `
"import { resolveComponent as _resolveComponent, createComponent as _createComponent, resolveDirective as _resolveDirective, withDirectives as _withDirectives, insert as _insert, createIf as _createIf, template as _template } from 'vue/vapor';
"import { resolveComponent as _resolveComponent, resolveDirective as _resolveDirective, createComponent as _createComponent, withDirectives as _withDirectives, insert as _insert, createIf as _createIf, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>")
export function render(_ctx) {
const _component_Bar = _resolveComponent("Bar")
const _component_Comp = _resolveComponent("Comp")
const _directive_hello = _resolveDirective("hello")
const _directive_test = _resolveDirective("test")
const n4 = _createComponent(_component_Comp, null, { default: () => {
const n0 = _createIf(() => (true), () => {
const n3 = t0()
const n2 = _createComponent(_component_Bar)
_withDirectives(n2, [[_resolveDirective("vHello"), void 0, void 0, { world: true }]])
_withDirectives(n2, [[_directive_hello, void 0, void 0, { world: true }]])
_insert(n2, n3)
return n3
})
return n0
} }, null, true)
_withDirectives(n4, [[_resolveDirective("vTest")]])
_withDirectives(n4, [[_directive_test]])
return n4
}"
`;

View File

@ -71,9 +71,10 @@ export function render(_ctx) {
`;
exports[`compiler: transform <slot> outlets > error on unexpected custom directive on <slot> 1`] = `
"import { createSlot as _createSlot } from 'vue/vapor';
"import { resolveDirective as _resolveDirective, createSlot as _createSlot } from 'vue/vapor';
export function render(_ctx) {
const _directive_foo = _resolveDirective("foo")
const n0 = _createSlot("default", null)
return n0
}"

View File

@ -36,7 +36,7 @@ describe('compiler: element transform', () => {
type: IRNodeTypes.CREATE_COMPONENT_NODE,
id: 0,
tag: 'Foo',
resolve: true,
asset: true,
root: true,
props: [[]],
},
@ -66,7 +66,7 @@ describe('compiler: element transform', () => {
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
tag: 'Example',
resolve: false,
asset: false,
},
])
})
@ -172,7 +172,7 @@ describe('compiler: element transform', () => {
type: IRNodeTypes.CREATE_COMPONENT_NODE,
id: 0,
tag: 'Example',
resolve: true,
asset: true,
},
])
})
@ -212,7 +212,7 @@ describe('compiler: element transform', () => {
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
tag: 'Foo',
resolve: true,
asset: true,
root: true,
props: [
[

View File

@ -1,4 +1,4 @@
import type { BlockIRNode } from '../ir'
import type { BlockIRNode, VaporHelper } from '../ir'
import {
type CodeFragment,
DELIMITERS_ARRAY,
@ -12,6 +12,7 @@ import {
import type { CodegenContext } from '../generate'
import { genEffects, genOperations } from './operation'
import { genChildren } from './template'
import { toValidAssetId } from '@vue/compiler-dom'
export function genBlock(
oper: BlockIRNode,
@ -43,16 +44,8 @@ export function genBlockContent(
const resetBlock = context.enterBlock(block)
if (root) {
for (const name of context.ir.component) {
push(
NEWLINE,
`const _component_${name} = `,
...genCall(
context.vaporHelper('resolveComponent'),
JSON.stringify(name),
),
)
}
genResolveAssets('component', 'resolveComponent')
genResolveAssets('directive', 'resolveDirective')
}
for (const child of dynamic.children) {
@ -77,4 +70,17 @@ export function genBlockContent(
resetBlock()
return frag
function genResolveAssets(
kind: 'component' | 'directive',
helper: VaporHelper,
) {
for (const name of context.ir[kind]) {
push(
NEWLINE,
`const ${toValidAssetId(name, kind)} = `,
...genCall(context.vaporHelper(helper), JSON.stringify(name)),
)
}
}
}

View File

@ -21,7 +21,7 @@ import {
} from './utils'
import { genExpression } from './expression'
import { genPropKey } from './prop'
import { createSimpleExpression } from '@vue/compiler-dom'
import { createSimpleExpression, toValidAssetId } from '@vue/compiler-dom'
import { genEventHandler } from './event'
import { genDirectiveModifiers, genDirectivesForElement } from './directive'
import { genModelHandler } from './modelValue'
@ -52,8 +52,8 @@ export function genCreateComponent(
]
function genTag() {
if (oper.resolve) {
return [`_component_${oper.tag}`]
if (oper.asset) {
return toValidAssetId(oper.tag, 'component')
} else {
return genExpression(
extend(createSimpleExpression(oper.tag, false), { ast: null }),

View File

@ -1,5 +1,9 @@
import { createSimpleExpression, isSimpleIdentifier } from '@vue/compiler-dom'
import { camelize } from '@vue/shared'
import {
createSimpleExpression,
isSimpleIdentifier,
toValidAssetId,
} from '@vue/compiler-dom'
import { extend } from '@vue/shared'
import { genExpression } from './expression'
import type { CodegenContext } from '../generate'
import {
@ -36,7 +40,12 @@ export function genWithDirective(
...genCall(vaporHelper('withDirectives'), element, directives),
]
function genDirective({ dir, builtin }: WithDirectiveIRNode): CodeFragment[] {
function genDirective({
dir,
name,
builtin,
asset,
}: WithDirectiveIRNode): CodeFragment[] {
const directive = genDirective()
const value = dir.exp && ['() => ', ...genExpression(dir.exp, context)]
const argument = dir.arg && genExpression(dir.arg, context)
@ -55,24 +64,15 @@ export function genWithDirective(
)
function genDirective() {
const {
vaporHelper,
options: { bindingMetadata },
} = context
if (dir.name === 'show') {
return [vaporHelper('vShow')]
} else if (builtin) {
return [vaporHelper(builtin)]
if (builtin) {
return vaporHelper(name as any)
} else if (asset) {
return toValidAssetId(name, 'directive')
} else {
const directiveReference = camelize(`v-${dir.name}`)
// TODO resolve directive
if (bindingMetadata[directiveReference]) {
const directiveExpression = createSimpleExpression(directiveReference)
directiveExpression.ast = null
return genExpression(directiveExpression, context)
} else {
return `${vaporHelper('resolveDirective')}("${directiveReference}")`
}
return genExpression(
extend(createSimpleExpression(name, false), { ast: null }),
context,
)
}
}
}

View File

@ -60,6 +60,7 @@ export interface RootIRNode {
source: string
template: string[]
component: Set<string>
directive: Set<string>
block: BlockIRNode
}
@ -197,7 +198,9 @@ export interface WithDirectiveIRNode extends BaseIRNode {
type: IRNodeTypes.WITH_DIRECTIVE
element: number
dir: VaporDirectiveNode
builtin?: VaporHelper
name: string
builtin?: boolean
asset?: boolean
}
export interface ComponentSlotBlockIRNode extends BlockIRNode {
@ -219,7 +222,7 @@ export interface CreateComponentIRNode extends BaseIRNode {
slots?: ComponentSlots
dynamicSlots?: ComponentDynamicSlot[]
resolve: boolean
asset: boolean
root: boolean
}

View File

@ -79,6 +79,8 @@ export class TransformContext<T extends AllNode = AllNode> {
comment: CommentNode[] = []
component: Set<string> = this.ir.component
directive: Set<string> = this.ir.directive
slots?: ComponentSlots
dynamicSlots?: ComponentDynamicSlot[]
@ -220,6 +222,7 @@ export function transform(
source: node.source,
template: [],
component: new Set(),
directive: new Set(),
block: newBlock(node),
}

View File

@ -72,24 +72,24 @@ function transformComponentElement(
propsResult: PropsResult,
context: TransformContext,
) {
let resolve = true
let asset = true
if (!__BROWSER__) {
const fromSetup = resolveSetupReference(tag, context)
if (fromSetup) {
tag = fromSetup
resolve = false
asset = false
}
const dotIndex = tag.indexOf('.')
if (dotIndex > 0) {
const ns = resolveSetupReference(tag.slice(0, dotIndex), context)
if (ns) {
tag = ns + tag.slice(dotIndex)
resolve = false
asset = false
}
}
}
if (resolve) {
if (asset) {
context.component.add(tag)
}
@ -102,7 +102,7 @@ function transformComponentElement(
id: context.reference(),
tag,
props: propsResult[0] ? propsResult[1] : [propsResult[1]],
resolve,
asset,
root,
slots: context.slots,
dynamicSlots: context.dynamicSlots,
@ -287,7 +287,7 @@ function transformProp(
node: ElementNode,
context: TransformContext<ElementNode>,
): DirectiveTransformResult | void {
const { name } = prop
let { name } = prop
if (prop.type === NodeTypes.ATTRIBUTE) {
if (isReservedProp(name)) return
@ -305,10 +305,20 @@ function transformProp(
}
if (!isBuiltInDirective(name)) {
const fromSetup =
!__BROWSER__ && resolveSetupReference(`v-${name}`, context)
if (fromSetup) {
name = fromSetup
} else {
context.directive.add(name)
}
context.registerOperation({
type: IRNodeTypes.WITH_DIRECTIVE,
element: context.reference(),
dir: prop,
name,
asset: !fromSetup,
})
}
}

View File

@ -62,8 +62,6 @@ export const transformVModel: DirectiveTransform = (dir, node, context) => {
}
const isComponent = node.tagType === ElementTypes.COMPONENT
let runtimeDirective: VaporHelper | undefined
if (isComponent) {
return {
key: arg ? arg : createSimpleExpression('modelValue', true),
@ -71,7 +69,8 @@ export const transformVModel: DirectiveTransform = (dir, node, context) => {
model: true,
modelModifiers: dir.modifiers,
}
} else {
}
if (dir.arg)
context.options.onError(
createDOMCompilerError(
@ -81,7 +80,7 @@ export const transformVModel: DirectiveTransform = (dir, node, context) => {
)
const { tag } = node
const isCustomElement = context.options.isCustomElement(tag)
runtimeDirective = 'vModelText'
let runtimeDirective: VaporHelper | undefined = 'vModelText'
if (
tag === 'input' ||
tag === 'textarea' ||
@ -139,7 +138,6 @@ export const transformVModel: DirectiveTransform = (dir, node, context) => {
),
)
}
}
context.registerOperation({
type: IRNodeTypes.SET_MODEL_VALUE,
@ -154,7 +152,8 @@ export const transformVModel: DirectiveTransform = (dir, node, context) => {
type: IRNodeTypes.WITH_DIRECTIVE,
element: context.reference(),
dir,
builtin: runtimeDirective,
name: runtimeDirective,
builtin: true,
})
function checkDuplicatedValue() {

View File

@ -14,5 +14,7 @@ export const transformVShow: DirectiveTransform = (dir, node, context) => {
type: IRNodeTypes.WITH_DIRECTIVE,
element: context.reference(),
dir,
name: 'vShow',
builtin: true,
})
}