From e2b51d6e7a5030b4e1b246a00c07d4c9062caa27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Mon, 13 May 2024 02:08:58 +0800 Subject: [PATCH] feat(compiler-vapor): resolve directive --- .../__snapshots__/compile.spec.ts.snap | 12 +- .../transformSlotOutlet.spec.ts.snap | 3 +- .../transforms/transformElement.spec.ts | 8 +- .../compiler-vapor/src/generators/block.ts | 28 ++-- .../src/generators/component.ts | 6 +- .../src/generators/directive.ts | 40 +++--- packages/compiler-vapor/src/ir.ts | 7 +- packages/compiler-vapor/src/transform.ts | 3 + .../src/transforms/transformElement.ts | 22 ++- .../compiler-vapor/src/transforms/vModel.ts | 127 +++++++++--------- .../compiler-vapor/src/transforms/vShow.ts | 2 + 11 files changed, 143 insertions(+), 115 deletions(-) diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap index 9e401d0ba..4acc91a94 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap @@ -16,30 +16,34 @@ exports[`compile > custom directive > basic 1`] = ` const t0 = _template("
") 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("
") 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 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformSlotOutlet.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformSlotOutlet.spec.ts.snap index 8d0b2ade5..eab69c82b 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformSlotOutlet.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformSlotOutlet.spec.ts.snap @@ -71,9 +71,10 @@ export function render(_ctx) { `; exports[`compiler: transform outlets > error on unexpected custom directive on 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 }" diff --git a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts index 248241246..bb5b8970f 100644 --- a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts @@ -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: [ [ diff --git a/packages/compiler-vapor/src/generators/block.ts b/packages/compiler-vapor/src/generators/block.ts index 41b2b0bda..3ce28cc1d 100644 --- a/packages/compiler-vapor/src/generators/block.ts +++ b/packages/compiler-vapor/src/generators/block.ts @@ -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)), + ) + } + } } diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index b48763266..ea626505c 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -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 }), diff --git a/packages/compiler-vapor/src/generators/directive.ts b/packages/compiler-vapor/src/generators/directive.ts index 7fdcb7bbf..fddfc8e32 100644 --- a/packages/compiler-vapor/src/generators/directive.ts +++ b/packages/compiler-vapor/src/generators/directive.ts @@ -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, + ) } } } diff --git a/packages/compiler-vapor/src/ir.ts b/packages/compiler-vapor/src/ir.ts index 01ea36d0c..27420d7f6 100644 --- a/packages/compiler-vapor/src/ir.ts +++ b/packages/compiler-vapor/src/ir.ts @@ -60,6 +60,7 @@ export interface RootIRNode { source: string template: string[] component: Set + directive: Set 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 } diff --git a/packages/compiler-vapor/src/transform.ts b/packages/compiler-vapor/src/transform.ts index 893efce1f..48b00ffb7 100644 --- a/packages/compiler-vapor/src/transform.ts +++ b/packages/compiler-vapor/src/transform.ts @@ -79,6 +79,8 @@ export class TransformContext { comment: CommentNode[] = [] component: Set = this.ir.component + directive: Set = 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), } diff --git a/packages/compiler-vapor/src/transforms/transformElement.ts b/packages/compiler-vapor/src/transforms/transformElement.ts index c16ddd2e7..f79102405 100644 --- a/packages/compiler-vapor/src/transforms/transformElement.ts +++ b/packages/compiler-vapor/src/transforms/transformElement.ts @@ -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, ): 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, }) } } diff --git a/packages/compiler-vapor/src/transforms/vModel.ts b/packages/compiler-vapor/src/transforms/vModel.ts index 72382ffb4..5f46aa786 100644 --- a/packages/compiler-vapor/src/transforms/vModel.ts +++ b/packages/compiler-vapor/src/transforms/vModel.ts @@ -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,74 +69,74 @@ export const transformVModel: DirectiveTransform = (dir, node, context) => { model: true, modelModifiers: dir.modifiers, } - } else { - if (dir.arg) - context.options.onError( - createDOMCompilerError( - DOMErrorCodes.X_V_MODEL_ARG_ON_ELEMENT, - dir.arg.loc, - ), - ) - const { tag } = node - const isCustomElement = context.options.isCustomElement(tag) - runtimeDirective = 'vModelText' - if ( - tag === 'input' || - tag === 'textarea' || - tag === 'select' || - isCustomElement - ) { - if (tag === 'input' || isCustomElement) { - const type = findProp(node, 'type') - if (type) { - if (type.type === NodeTypes.DIRECTIVE) { - // :type="foo" - runtimeDirective = 'vModelDynamic' - } else if (type.value) { - switch (type.value.content) { - case 'radio': - runtimeDirective = 'vModelRadio' - break - case 'checkbox': - runtimeDirective = 'vModelCheckbox' - break - case 'file': - runtimeDirective = undefined - context.options.onError( - createDOMCompilerError( - DOMErrorCodes.X_V_MODEL_ON_FILE_INPUT_ELEMENT, - dir.loc, - ), - ) - break - default: - // text type - __DEV__ && checkDuplicatedValue() - break - } - } - } else if (hasDynamicKeyVBind(node)) { - // element has bindings with dynamic keys, which can possibly contain - // "type". + } + + if (dir.arg) + context.options.onError( + createDOMCompilerError( + DOMErrorCodes.X_V_MODEL_ARG_ON_ELEMENT, + dir.arg.loc, + ), + ) + const { tag } = node + const isCustomElement = context.options.isCustomElement(tag) + let runtimeDirective: VaporHelper | undefined = 'vModelText' + if ( + tag === 'input' || + tag === 'textarea' || + tag === 'select' || + isCustomElement + ) { + if (tag === 'input' || isCustomElement) { + const type = findProp(node, 'type') + if (type) { + if (type.type === NodeTypes.DIRECTIVE) { + // :type="foo" runtimeDirective = 'vModelDynamic' - } else { - // text type - __DEV__ && checkDuplicatedValue() + } else if (type.value) { + switch (type.value.content) { + case 'radio': + runtimeDirective = 'vModelRadio' + break + case 'checkbox': + runtimeDirective = 'vModelCheckbox' + break + case 'file': + runtimeDirective = undefined + context.options.onError( + createDOMCompilerError( + DOMErrorCodes.X_V_MODEL_ON_FILE_INPUT_ELEMENT, + dir.loc, + ), + ) + break + default: + // text type + __DEV__ && checkDuplicatedValue() + break + } } - } else if (tag === 'select') { - runtimeDirective = 'vModelSelect' + } else if (hasDynamicKeyVBind(node)) { + // element has bindings with dynamic keys, which can possibly contain + // "type". + runtimeDirective = 'vModelDynamic' } else { - // textarea + // text type __DEV__ && checkDuplicatedValue() } + } else if (tag === 'select') { + runtimeDirective = 'vModelSelect' } else { - context.options.onError( - createDOMCompilerError( - DOMErrorCodes.X_V_MODEL_ON_INVALID_ELEMENT, - dir.loc, - ), - ) + // textarea + __DEV__ && checkDuplicatedValue() } + } else { + context.options.onError( + createDOMCompilerError( + DOMErrorCodes.X_V_MODEL_ON_INVALID_ELEMENT, + dir.loc, + ), + ) } context.registerOperation({ @@ -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() { diff --git a/packages/compiler-vapor/src/transforms/vShow.ts b/packages/compiler-vapor/src/transforms/vShow.ts index 7be355396..0553a3de7 100644 --- a/packages/compiler-vapor/src/transforms/vShow.ts +++ b/packages/compiler-vapor/src/transforms/vShow.ts @@ -14,5 +14,7 @@ export const transformVShow: DirectiveTransform = (dir, node, context) => { type: IRNodeTypes.WITH_DIRECTIVE, element: context.reference(), dir, + name: 'vShow', + builtin: true, }) }