From 88f0e0b657d925184c434cc009526434c098a220 Mon Sep 17 00:00:00 2001 From: Zayooo Date: Mon, 2 Nov 2026 13:18:52 +0100 Subject: [PATCH] [wab] don't leave blank lines between generated JSX attributes makeCreatePlasmicElement interpolates every optional attribute (data-plasmic-root, data-plasmic-for-node, trigger props) as an empty string when it doesn't apply, leaving empty lines in the element. The CLI used to hide them because prettier removed blank lines between JSX attributes, but prettier 3.9 keeps one (prettier/prettier#19161), so since @plasmicapp/cli 0.1.380 every synced non-root element gets an empty line after data-plasmic-override. Only join the attributes that are present. --- .../__snapshots__/animations.test.ts.snap | 22 - .../aria-code-components.test.ts.snap | 428 ------------------ .../__snapshots__/data-tokens.test.ts.snap | 18 - ...-project-default-theme-styles.test.ts.snap | 166 ------- .../__snapshots__/linked-props.test.ts.snap | 64 --- .../style-token-overrides.test.ts.snap | 14 - .../wab/shared/codegen/react-p/index.test.ts | 35 ++ .../src/wab/shared/codegen/react-p/index.ts | 55 ++- 8 files changed, 61 insertions(+), 741 deletions(-) diff --git a/platform/wab/src/wab/shared/codegen/__snapshots__/animations.test.ts.snap b/platform/wab/src/wab/shared/codegen/__snapshots__/animations.test.ts.snap index d257ab24f15..2c5df56a722 100644 --- a/platform/wab/src/wab/shared/codegen/__snapshots__/animations.test.ts.snap +++ b/platform/wab/src/wab/shared/codegen/__snapshots__/animations.test.ts.snap @@ -330,25 +330,14 @@ const $$ = {}; data-plasmic-root={true} data-plasmic-for-node={forNode} className={classNames("plasmic_default__all", "root_reset_1234567890", "plasmic_default_styles", "plasmic_mixins", styleTokensClassNames, "Homepage__root__wN3Do")} - >

{ "This project animation" }

{ "Imported project animation" }

@@ -1331,25 +1320,14 @@ const $$ = {}; data-plasmic-root={true} data-plasmic-for-node={forNode} className={classNames("all", "root_reset_1234567890", "plasmic_default_styles", "plasmic_mixins", styleTokensClassNames, sty.root)} - >

{ "This project animation" }

{ "Imported project animation" }

diff --git a/platform/wab/src/wab/shared/codegen/__snapshots__/aria-code-components.test.ts.snap b/platform/wab/src/wab/shared/codegen/__snapshots__/aria-code-components.test.ts.snap index c9d0b4f0745..cf5f6f5cd0b 100644 --- a/platform/wab/src/wab/shared/codegen/__snapshots__/aria-code-components.test.ts.snap +++ b/platform/wab/src/wab/shared/codegen/__snapshots__/aria-code-components.test.ts.snap @@ -1285,7 +1285,6 @@ onPress={args.onClick} plasmicUpdateVariant={updateVariant} resetsForm={args.resetsForm} submitsForm={args.submitsForm} - > { (hasVariant( $state, @@ -1302,8 +1301,6 @@ submitsForm={args.submitsForm} ) ? true : false) ? (
) : null } { (hasVariant( $state, @@ -1350,8 +1346,6 @@ submitsForm={args.submitsForm} ) ? true : false) ? (
) : null }
{ renderPlasmicSlot({ defaultContents: , value: args.start, className: classNames("Button__slotTargetStart__hwuAw", { "Button__slotTargetStartcolor_errorDestructive__hwuAwcMheu": hasVariant( @@ -1704,12 +1679,7 @@ role={"img"}
{ renderPlasmicSlot({ defaultContents:
{ "Text" }
, @@ -1849,10 +1819,6 @@ role={"img"} ) }) }) }
{ renderPlasmicSlot({ defaultContents: , value: args.end, className: classNames("Button__slotTargetEnd__ooNSu", { "Button__slotTargetEndcolor_errorDestructive__ooNSucMheu": hasVariant( @@ -2315,7 +2275,6 @@ const $$ = {}; data-plasmic-root={true} data-plasmic-for-node={forNode} className={classNames("__wab_instance", "DataFetcherGrandParent__root__oy7X5")} - /> ) as React.ReactElement | null; } @@ -2606,7 +2565,6 @@ const $$ = {}; data-plasmic-root={true} data-plasmic-for-node={forNode} queries={{}} - /> ) as React.ReactElement | null; } @@ -2910,15 +2868,11 @@ const $$ = {}; data-plasmic-root={true} data-plasmic-for-node={forNode} className={classNames("plasmic_default__all", "root_reset_1234567890", "plasmic_default_styles", "plasmic_mixins", styleTokensClassNames, "HelloWorld__root__cgR3R")} - >
{ "My world" }
@@ -3554,8 +3508,6 @@ const $$ = {}; dialog={ { }} ref={(ref) => { $refs["ariaModal"] = ref; }} resetClassName={classNames("root_reset_1234567890", "plasmic_default_styles", "plasmic_mixins", styleTokensClassNames)} - >
{ (( $props.showHeader )) ? ( { renderPlasmicSlot({ defaultContents: "Heading", @@ -3605,37 +3548,19 @@ slot={"title"}
{ renderPlasmicSlot({ defaultContents:
{ "This is a Modal!" }
{ "You can put anything here!\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." }
@@ -3647,35 +3572,16 @@ slot={"title"} { (( $props.showFooter )) ? (
{ renderPlasmicSlot({ defaultContents:
} @@ -3714,7 +3620,6 @@ onClick={async (event) => { }} type={"soft"} - />
, value: args.footer, @@ -3737,17 +3642,11 @@ trigger={(hasVariant( "noTrigger" ) ? false : true) ? (renderPlasmicSlot({ defaultContents:
} @@ -10556,7 +10248,6 @@ onClick={async (event) => { }} type={"soft"} - />
, value: args.footer, @@ -10579,17 +10270,11 @@ trigger={(hasVariant( "noTrigger" ) ? false : true) ? (renderPlasmicSlot({ defaultContents:
@@ -1512,15 +1511,11 @@ const $$ = {}; data-plasmic-root={true} data-plasmic-for-node={forNode} className={classNames("plasmic_default__all", "root_reset_1234567890", "plasmic_default_styles", "plasmic_mixins", styleTokensClassNames, "P1__root__nReBx")} - >
{(() => { try { @@ -1540,10 +1535,7 @@ const $$ = {};
{(() => { try { @@ -1561,12 +1553,7 @@ const $$ = {}; })()}
{(() => { try { @@ -1584,12 +1571,7 @@ const $$ = {}; })()}
{(() => { try { diff --git a/platform/wab/src/wab/shared/codegen/__snapshots__/imported-project-default-theme-styles.test.ts.snap b/platform/wab/src/wab/shared/codegen/__snapshots__/imported-project-default-theme-styles.test.ts.snap index 2a74ec82290..64c86117e39 100644 --- a/platform/wab/src/wab/shared/codegen/__snapshots__/imported-project-default-theme-styles.test.ts.snap +++ b/platform/wab/src/wab/shared/codegen/__snapshots__/imported-project-default-theme-styles.test.ts.snap @@ -449,118 +449,65 @@ const $$ = {}; data-plasmic-root={true} data-plasmic-for-node={forNode} className={classNames("plasmic_default__all", "root_reset_1234567890", "plasmic_default_styles", "plasmic_mixins", styleTokensClassNames, "Homepage__root__rIWL")} - >
{ "Default text" }
{ "Default text span" }

{ "Welcome to your first page." }

{"If you haven't already done so, go back and learn the basics by going through the Plasmic Levels tutorial.\\n\\nIt's always easier to start from examples! Add a new page using a template\\u2014do this from the list of pages in the top toolbar.\\n\\nOr press the big blue + button to start inserting items into this page.\\n\\nIntegrate this project into your codebase\\u2014press the "}{"Code"}{" button in the top right and follow the quickstart instructions.\\n\\nJoin our Slack community (icon in bottom left) for help any time."}
{ "Enter some text" }
{ "Enter some text" }
{ "Enter some text" }
{ "Enter some text" }
@@ -568,28 +515,15 @@ const $$ = {};
{ "Enter some text" }
{ "Enter some text" }
@@ -597,28 +531,15 @@ const $$ = {};
{ "Enter some text" }
{ "Enter some text" }
@@ -991,15 +912,11 @@ const $$ = {}; data-plasmic-root={true} data-plasmic-for-node={forNode} className={classNames("plasmic_default__all", "root_reset_1234567890", "plasmic_default_styles", "plasmic_mixins", styleTokensClassNames, "Test__root__wvcyx")} - >
{ renderPlasmicSlot({ defaultContents: "Enter some text", @@ -2100,118 +2017,65 @@ const $$ = {}; data-plasmic-root={true} data-plasmic-for-node={forNode} className={classNames("all", "root_reset_1234567890", "plasmic_default_styles", "plasmic_mixins", styleTokensClassNames, sty.root)} - >
{ "Default text" }
{ "Default text span" }

{ "Welcome to your first page." }

{"If you haven't already done so, go back and learn the basics by going through the Plasmic Levels tutorial.\\n\\nIt's always easier to start from examples! Add a new page using a template\\u2014do this from the list of pages in the top toolbar.\\n\\nOr press the big blue + button to start inserting items into this page.\\n\\nIntegrate this project into your codebase\\u2014press the "}{"Code"}{" button in the top right and follow the quickstart instructions.\\n\\nJoin our Slack community (icon in bottom left) for help any time."}
{ "Enter some text" }
{ "Enter some text" }
{ "Enter some text" }
{ "Enter some text" }
@@ -2219,28 +2083,15 @@ const $$ = {};
{ "Enter some text" }
{ "Enter some text" }
@@ -2248,28 +2099,15 @@ const $$ = {};
{ "Enter some text" }
{ "Enter some text" }
@@ -2642,15 +2480,11 @@ const $$ = {}; data-plasmic-root={true} data-plasmic-for-node={forNode} className={classNames("all", "root_reset_1234567890", "plasmic_default_styles", "plasmic_mixins", styleTokensClassNames, sty.root)} - >
{ renderPlasmicSlot({ defaultContents: "Enter some text", diff --git a/platform/wab/src/wab/shared/codegen/__snapshots__/linked-props.test.ts.snap b/platform/wab/src/wab/shared/codegen/__snapshots__/linked-props.test.ts.snap index ac0a3e7d677..f10be8fc9c6 100644 --- a/platform/wab/src/wab/shared/codegen/__snapshots__/linked-props.test.ts.snap +++ b/platform/wab/src/wab/shared/codegen/__snapshots__/linked-props.test.ts.snap @@ -367,39 +367,24 @@ const $$ = {}; "theme", "dark" ) })} - > { (hasVariant( $state, "locked", "locked" ) ? false : true) ? (
{ "Username: " }
{(() => { try { @@ -432,32 +416,18 @@ const $$ = {}; "locked", "locked" ) ? false : true) ? (
{ "Theme: " }
{ hasVariant( $state, @@ -493,16 +462,11 @@ const $$ = {}; "locked", "locked" ) ? true : false) ? (
{ "The content is locked" }
) : null } @@ -808,18 +772,14 @@ const $$ = {}; data-plasmic-root={true} data-plasmic-for-node={forNode} className={classNames("plasmic_default__all", "root_reset_1234567890", "plasmic_default_styles", "plasmic_mixins", styleTokensClassNames, "Outer__root___634Ea")} - >
) as React.ReactElement | null; @@ -1184,57 +1144,33 @@ const $$ = {}; data-plasmic-root={true} data-plasmic-for-node={forNode} className={classNames("plasmic_default__all", "root_reset_1234567890", "plasmic_default_styles", "plasmic_mixins", styleTokensClassNames, "PropLinkingTest__root__aWcUo")} - >
{ "Prop Linking Test" }
diff --git a/platform/wab/src/wab/shared/codegen/__snapshots__/style-token-overrides.test.ts.snap b/platform/wab/src/wab/shared/codegen/__snapshots__/style-token-overrides.test.ts.snap index 15ddfd11e62..29d34bf1661 100644 --- a/platform/wab/src/wab/shared/codegen/__snapshots__/style-token-overrides.test.ts.snap +++ b/platform/wab/src/wab/shared/codegen/__snapshots__/style-token-overrides.test.ts.snap @@ -395,23 +395,16 @@ const $$ = {}; "fontSize", "large" ) })} - >

{ "Main text (primary)" }

@@ -1472,23 +1465,16 @@ const $$ = {}; "fontSize", "large" ) })} - >

{ "Main text (primary)" }

diff --git a/platform/wab/src/wab/shared/codegen/react-p/index.test.ts b/platform/wab/src/wab/shared/codegen/react-p/index.test.ts index 071c5b19158..a306b47d9a8 100644 --- a/platform/wab/src/wab/shared/codegen/react-p/index.test.ts +++ b/platform/wab/src/wab/shared/codegen/react-p/index.test.ts @@ -30,6 +30,7 @@ import { import { typeFactory } from "@/wab/shared/model/model-util"; import { PlasmicTranslatorContext } from "@plasmicapp/host"; import { render } from "@testing-library/react"; +import * as prettier from "prettier"; import * as React from "react"; import tmp from "tmp"; @@ -86,6 +87,40 @@ describe("serializeTplTagBase", () => { expect(submitButtonJsx).not.toContain('type={"button"}'); }); + it("leaves no blank lines between the attributes of non-root elements once formatted", async () => { + const site = createSite(); + const component = mkComponent({ + name: "Card", + type: ComponentType.Plain, + tplTree: (baseVariant) => { + const title = mkTplTagX("button", { + name: "title", + baseVariant, + attrs: { title: codeLit("Open") }, + }); + const root = mkTplTagX("div", {}, [title]); + ensureVariantSetting(root, [baseVariant]); + return root; + }, + }); + site.components = [...site.components, component]; + + const { readFromProject } = await codegen(dir.name, site); + const formatted = await prettier.format( + readFromProject("PlasmicCard.tsx"), + { + parser: "typescript", + }, + ); + + const titleJsx = formatted.slice( + formatted.indexOf('data-plasmic-name={"title"}'), + ); + expect(titleJsx).toMatch( + /data-plasmic-override=\{overrides\.title\}\n[ \t]*className=/, + ); + }); + it("defaults alt to its image asset's description unless alt is set", async () => { const site = createSite(); const tplMgr = new TplMgr({ site }); diff --git a/platform/wab/src/wab/shared/codegen/react-p/index.ts b/platform/wab/src/wab/shared/codegen/react-p/index.ts index 419012ccfb0..9b148e49042 100644 --- a/platform/wab/src/wab/shared/codegen/react-p/index.ts +++ b/platform/wab/src/wab/shared/codegen/react-p/index.ts @@ -1943,36 +1943,33 @@ function makeCreatePlasmicElement( const isFakeTpl = ctx.fakeTpls.includes(node); const serializeComponentCall = (is$PropsCreated: boolean) => { + // Omitted attributes must not leave empty lines behind: prettier >= 3.9 + // keeps a blank line between JSX attributes, so they end up in the output. + const serializedAttrs = [ + !skinny && nodeName && !isFakeTpl + ? `data-plasmic-name={${jsLiteral(nodeName)}}` + : "", + nodeName && !isFakeTpl + ? `data-plasmic-override={${getDataPlasmicOverride( + nodeName, + isPlasmicDefaultNodeName, + )}}` + : "", + !skinny && isRoot ? `data-plasmic-root={true}` : "", + !skinny && isRoot ? `data-plasmic-for-node={forNode}` : "", + is$PropsCreated + ? `{...child$Props}` + : keys + .map((key) => `${serializedKeyValue(key, `${attrs[key]}`)}`) + .join("\n"), + triggeredHooks && triggeredHooks.length > 0 + ? `data-plasmic-trigger-props={[${L.uniq( + triggeredHooks.flatMap((spec) => spec.getTriggerPropNames()), + ).join(", ")}]}` + : "", + ].filter((serializedAttr) => serializedAttr !== ""); return `<${tagType} - ${ - !skinny && nodeName && !isFakeTpl - ? `data-plasmic-name={${jsLiteral(nodeName)}}` - : "" - } - ${ - nodeName && !isFakeTpl - ? `data-plasmic-override={${getDataPlasmicOverride( - nodeName, - isPlasmicDefaultNodeName, - )}}` - : "" - } - ${!skinny && isRoot ? `data-plasmic-root={true}` : ""} - ${!skinny && isRoot ? `data-plasmic-for-node={forNode}` : ""} - ${ - is$PropsCreated - ? `{...child$Props}` - : keys - .map((key) => `${serializedKeyValue(key, `${attrs[key]}`)}`) - .join("\n") - } - ${ - triggeredHooks && triggeredHooks.length > 0 - ? `data-plasmic-trigger-props={[${L.uniq( - triggeredHooks.flatMap((spec) => spec.getTriggerPropNames()), - ).join(", ")}]}` - : "" - } + ${serializedAttrs.join("\n ")} ${ hasChildren ? `>