diff --git a/.changeset/brave-beds-act.md b/.changeset/brave-beds-act.md new file mode 100644 index 0000000000..4288967ceb --- /dev/null +++ b/.changeset/brave-beds-act.md @@ -0,0 +1,5 @@ +--- +'@builder.io/mitosis': patch +--- + +Fix: properly handle innerHTML properties in react codegen diff --git a/packages/core/src/__tests__/__snapshots__/builder.test.ts.snap b/packages/core/src/__tests__/__snapshots__/builder.test.ts.snap index d94422bead..3f801630e6 100644 --- a/packages/core/src/__tests__/__snapshots__/builder.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/builder.test.ts.snap @@ -3530,6 +3530,39 @@ exports[`Builder > Text with bindings 3`] = ` } `; +exports[`Builder > Valid Custom Code 1`] = ` +" + +" +`; + +exports[`Builder > Valid Custom Code 2`] = ` +"import * as React from \\"react\\"; + +function MyComponent(props) { + return ( +
+
\\", + }} + /> +
+ ); +} + +export default MyComponent; +" +`; + exports[`Builder > async bindings 1`] = ` { "data": { diff --git a/packages/core/src/__tests__/builder.test.ts b/packages/core/src/__tests__/builder.test.ts index f19db5264d..98cf441b78 100644 --- a/packages/core/src/__tests__/builder.test.ts +++ b/packages/core/src/__tests__/builder.test.ts @@ -3,6 +3,7 @@ import { componentToHtml } from '@/generators/html'; import { componentToMitosis } from '@/generators/mitosis'; import { ToMitosisOptions } from '@/generators/mitosis/types'; import { componentToReact } from '@/generators/react'; +import { componentToVue } from '@/generators/vue'; import { dedent } from '@/helpers/dedent'; import { builderContentToMitosisComponent, @@ -322,6 +323,35 @@ describe('Builder', () => { expect(html).toMatchSnapshot(); }); + test('Valid Custom Code', async () => { + const builderJson: BuilderContent = { + data: { + blocks: [ + { + '@type': '@builder.io/sdk:Element', + component: { + name: 'CustomCode', + options: { + code: ``, + }, + }, + }, + ], + }, + } as BuilderContent; + const component = builderContentToMitosisComponent(builderJson); + + const vue = componentToVue({ + plugins: [compileAwayBuilderComponents()], + })({ component }); + expect(vue).toMatchSnapshot(); + + const react = componentToReact({ + plugins: [compileAwayBuilderComponents()], + })({ component }); + expect(react).toMatchSnapshot(); + }); + test('Regenerate custom Hero', () => { const code = dedent` import { Hero } from "@components"; diff --git a/packages/core/src/generators/react/blocks.ts b/packages/core/src/generators/react/blocks.ts index 40dc24b97d..04d2e7dcd6 100644 --- a/packages/core/src/generators/react/blocks.ts +++ b/packages/core/src/generators/react/blocks.ts @@ -145,7 +145,12 @@ const ATTTRIBUTE_MAPPERS: { [key: string]: string } = { const BINDING_MAPPERS: { [key: string]: | string - | ((key: string, value: string, options?: ToReactOptions) => [string, string]); + | (( + key: string, + value: string, + options?: ToReactOptions, + isProperty?: boolean, + ) => [string, string]); } = { ref(ref, value, options) { if (options?.preact) { @@ -161,8 +166,14 @@ const BINDING_MAPPERS: { } return [ref, value]; }, - innerHTML(_key, value) { - return ['dangerouslySetInnerHTML', `{__html: ${value.replace(/\s+/g, ' ')}}`]; + innerHTML(_key, value, _options, isProperty) { + const wrapChar = isProperty ? '"' : ''; + let useValue = value.replace(/\s+/g, ' '); + + if (isProperty) { + useValue = value.replace(/"/g, '\\"'); + } + return ['dangerouslySetInnerHTML', `{__html: ${wrapChar}${useValue}${wrapChar}}`]; }, ...ATTTRIBUTE_MAPPERS, }; @@ -246,7 +257,7 @@ export const blockToReact = ( } else if (BINDING_MAPPERS[key]) { const mapper = BINDING_MAPPERS[key]; if (typeof mapper === 'function') { - const [newKey, newValue] = mapper(key, value, options); + const [newKey, newValue] = mapper(key, value, options, true); str += ` ${newKey}={${newValue}} `; } else { str += ` ${BINDING_MAPPERS[key]}="${value}" `; diff --git a/packages/core/src/plugins/compile-away-builder-components.ts b/packages/core/src/plugins/compile-away-builder-components.ts index ef9fb881a7..162791fa9d 100644 --- a/packages/core/src/plugins/compile-away-builder-components.ts +++ b/packages/core/src/plugins/compile-away-builder-components.ts @@ -203,7 +203,10 @@ export const components: CompileAwayComponentsMap = { bindings.innerHTML = node.bindings.code; } return wrapOutput( - node, + { + ...node, + properties: omit(node.properties, 'code'), + }, createMitosisNode({ name: (node.properties.builderTag as string) || 'div', properties: {