From 3a6768e0e3469e613113f80ad07594a737d357c8 Mon Sep 17 00:00:00 2001 From: Sami Jaber Date: Tue, 24 Jan 2023 15:40:11 -0400 Subject: [PATCH] Refactor: Vue bindings (#996) * tidy * more cleanup * refactor * fix fix * fix --- packages/core/src/generators/vue/blocks.ts | 118 +++++++++++---------- 1 file changed, 64 insertions(+), 54 deletions(-) diff --git a/packages/core/src/generators/vue/blocks.ts b/packages/core/src/generators/vue/blocks.ts index 2d4edd8de8..17873a2328 100644 --- a/packages/core/src/generators/vue/blocks.ts +++ b/packages/core/src/generators/vue/blocks.ts @@ -1,5 +1,6 @@ -import { pipe } from 'fp-ts/lib/function'; +import { pipe, identity } from 'fp-ts/lib/function'; import { filter } from 'lodash'; +import { Dictionary } from '../../helpers/typescript'; import { filterEmptyTextNodes } from '../../helpers/filter-empty-text-nodes'; import isChildren from '../../helpers/is-children'; import { isMitosisNode } from '../../helpers/is-mitosis-node'; @@ -21,6 +22,9 @@ const SPECIAL_PROPERTIES = { V_FOR: 'v-for', V_ELSE: 'v-else', V_ELSE_IF: 'v-else-if', + V_ON: 'v-on', + V_ON_AT: '@', + V_BIND: 'v-bind', } as const; type BlockRenderer = (json: MitosisNode, options: ToVueOptions, scope?: Scope) => string; @@ -283,11 +287,11 @@ const NODE_MAPPERS: { const stringifyBinding = (node: MitosisNode) => - ([key, value]: [string, Binding | undefined]) => { - if (node.bindings[key]?.type === 'spread') { + ([key, value]: [string, Binding]) => { + if (value.type === 'spread') { return ''; // we handle this after } else if (key === 'class') { - return ` :class="_classStringToObject(${value?.code})" `; + return `:class="_classStringToObject(${value?.code})"`; // TODO: support dynamic classes as objects somehow like Vue requires // https://vuejs.org/v2/guide/class-and-style.html } else { @@ -301,29 +305,66 @@ const stringifyBinding = event = 'input'; } const isAssignmentExpression = useValue.includes('='); - const valueWRenamedEvent = replaceIdentifiers({ - code: useValue, - from: cusArgs[0], - to: '$event', - }); - - // TODO: proper babel transform to replace. Util for this - if (isAssignmentExpression) { - return ` @${event}="${encodeQuotes(removeSurroundingBlock(valueWRenamedEvent))}" `; - } else { - return ` @${event}="${encodeQuotes( - removeSurroundingBlock(removeSurroundingBlock(valueWRenamedEvent)), - )}" `; - } + + const eventHandlerValue = pipe( + replaceIdentifiers({ + code: useValue, + from: cusArgs[0], + to: '$event', + }), + isAssignmentExpression ? identity : removeSurroundingBlock, + removeSurroundingBlock, + encodeQuotes, + ); + + const eventHandlerKey = `${SPECIAL_PROPERTIES.V_ON_AT}${event}`; + + return `${eventHandlerKey}="${eventHandlerValue}"`; } else if (key === 'ref') { - return ` ref="${encodeQuotes(useValue)}" `; + return `ref="${encodeQuotes(useValue)}"`; } else if (BINDING_MAPPERS[key]) { - return ` ${BINDING_MAPPERS[key]}="${encodeQuotes(useValue.replace(/"/g, "\\'"))}" `; + return `${BINDING_MAPPERS[key]}="${encodeQuotes(useValue.replace(/"/g, "\\'"))}"`; } else { - return ` :${key}="${encodeQuotes(useValue)}" `; + return `:${key}="${encodeQuotes(useValue)}"`; } } }; + +const stringifySpreads = (node: MitosisNode) => { + let spreads = filter(node.bindings, (binding) => binding?.type === 'spread').map((value) => + value!.code === 'props' ? '$props' : value!.code, + ); + + if (spreads.length === 0) { + return ''; + } + + const stringifiedValue = + spreads.length > 1 ? `{${spreads.map((spread) => `...${spread}`).join(', ')}}` : spreads[0]; + + return ` ${SPECIAL_PROPERTIES.V_BIND}="${encodeQuotes(stringifiedValue)}" `; +}; + +const getBlockBindings = (node: MitosisNode) => { + const stringifiedProperties = Object.entries(node.properties) + .map(([key, value]) => { + if (key === 'className') { + return ''; + } else if (key === SPECIAL_PROPERTIES.V_ELSE) { + return `${key}`; + } else if (typeof value === 'string') { + return `${key}="${encodeQuotes(value)}"`; + } + }) + .join(' '); + + const stringifiedBindings = Object.entries(node.bindings as Dictionary) + .map(stringifyBinding(node)) + .join(' '); + + return [stringifiedProperties, stringifiedBindings, stringifySpreads(node)].join(' '); +}; + export const blockToVue: BlockRenderer = (node, options, scope) => { const nodeMapper = NODE_MAPPERS[node.name]; if (nodeMapper) { @@ -353,40 +394,9 @@ export const blockToVue: BlockRenderer = (node, options, scope) => { return `{{${textCode}}}`; } - let str = ''; - - str += `<${node.name} `; - - for (const key in node.properties) { - const value = node.properties[key]; - - if (key === 'className') { - continue; - } else if (key === SPECIAL_PROPERTIES.V_ELSE) { - str += ` ${key} `; - } else if (typeof value === 'string') { - str += ` ${key}="${encodeQuotes(value)}" `; - } - } - - const stringifiedBindings = Object.entries(node.bindings).map(stringifyBinding(node)).join(''); + let str = `<${node.name} `; - str += stringifiedBindings; - - // spreads - - let spreads = filter(node.bindings, (binding) => binding?.type === 'spread').map((value) => - value?.code === 'props' ? '$props' : value?.code, - ); - - if (spreads?.length) { - if (spreads.length > 1) { - let spreadsString = `{...${spreads.join(', ...')}}`; - str += ` v-bind="${encodeQuotes(spreadsString)}"`; - } else { - str += ` v-bind="${encodeQuotes(spreads.join(''))}"`; - } - } + str += getBlockBindings(node); if (selfClosingTags.has(node.name)) { return str + ' />';