Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue SDK: stop passing down unused props #3891

Merged
merged 19 commits into from
Mar 27, 2025
5 changes: 5 additions & 0 deletions .changeset/calm-keys-greet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@builder.io/sdk-vue": patch
---

Fix: extra props warnings
11 changes: 11 additions & 0 deletions .changeset/little-masks-allow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@builder.io/sdk-angular': patch
'@builder.io/sdk-react-nextjs': patch
'@builder.io/sdk-qwik': patch
'@builder.io/sdk-react': patch
'@builder.io/sdk-solid': patch
'@builder.io/sdk-svelte': patch
'@builder.io/sdk-vue': patch
---

Fix: stop passing `builderContext` to Text block
38 changes: 32 additions & 6 deletions packages/sdks-tests/src/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ async function screenshotOnFailure(
}
}

const isIgnorableError = (error: Error) => {
return error.message.includes(
/**
* This error started appearing recently across all frameworks.
* It is most likely some playwright browser issue and not something we can fix in our code.
*/
"Failed to execute 'observe' on 'PressureObserver': Access to the feature \"compute pressure\" is disallowed by permissions policy"
);
};

const test = base.extend<TestOptions>({
// this is provided by `playwright.config.ts`
packageName: ['DEFAULT' as any, { option: true }],
Expand All @@ -45,19 +55,23 @@ const test = base.extend<TestOptions>({
ignoreHydrationErrors: [false, { option: true }],
page: async ({ context, page, packageName, sdk, ignoreHydrationErrors }, use) => {
if (packageName === ('DEFAULT' as any)) {
throw new Error('packageName is required');
throw new Error('`packageName` is required but was not provided.');
}
if (sdk === ('DEFAULT' as any)) {
throw new Error('sdk is required');
throw new Error('`sdk` is required but was not provided.');
}

context.on('weberror', err => {
if (isIgnorableError(err.error())) return;

console.error(err.error());
throw new Error('Failing test due to error in browser: ' + err.error());
throw new Error('Test failed due to error thrown in browser: ' + err.error());
});
page.on('pageerror', err => {
if (isIgnorableError(err)) return;

console.error(err);
throw new Error('Failing test due to error in browser: ' + err);
throw new Error('Test failed due to error thrown in browser: ' + err);
});

/**
Expand All @@ -80,16 +94,27 @@ const test = base.extend<TestOptions>({
}
});
}

if (sdk === 'angular') {
page.on('console', msg => {
const originalText = msg.text();
if (originalText.includes('NG0303')) {
throw new Error('Angular input not annotated error detected: ' + originalText);
}
});
} else if (sdk === 'vue') {
page.on('console', msg => {
const originalText = msg.text();
if (originalText.toLowerCase().includes('[vue warn]:')) {
throw new Error('Vue warning detected: ' + originalText);
}
});
context.on('console', msg => {
const originalText = msg.text();
if (originalText.toLowerCase().includes('[vue warn]:')) {
throw new Error('Vue warning detected: ' + originalText);
}
});
}

await use(page);
},
});
Expand Down Expand Up @@ -219,6 +244,7 @@ export const checkIfIsHydrationErrorMessage = (_text: string) => {
const text = _text.toLowerCase();
const isVueHydrationMismatch =
text.includes('[vue warn]') && (text.includes('hydration') || text.includes('mismatch'));

const isReactHydrationMismatch =
text.includes('did not expect server') ||
text.includes('content does not match') ||
Expand Down
39 changes: 26 additions & 13 deletions packages/sdks/mitosis.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const rng = seedrandom('vue-sdk-seed');
* @typedef {import('@builder.io/mitosis').MitosisNode} MitosisNode
* @typedef {import('@builder.io/mitosis').StateValue} StateValue
* @typedef {import('@builder.io/mitosis').MitosisConfig} MitosisConfig
* @typedef {import('@builder.io/mitosis').Plugin} Plugin
* @typedef {import('@builder.io/mitosis').MitosisPlugin} Plugin
* @typedef {import('@builder.io/mitosis').OnMountHook} OnMountHook
*/

Expand Down Expand Up @@ -313,17 +313,13 @@ const filterActionAttrBindings = (json, item) => {
/**
* @type {Plugin}
*/
const ANGULAR_ADD_UNUSED_PROP_TYPES = () => ({
const REMOVE_UNUSED_PROPS_HACK_PLUGIN = () => ({
json: {
post: (json) => {
if (json.name === 'Awaiter') {
json.hooks.onMount = json.hooks.onMount.filter(
(hook) =>
!hook.code.includes(
'/** this is a hack to include the input in angular */'
)
);
}
json.hooks.onMount = json.hooks.onMount.filter(
(hook) =>
!hook.code.includes('/** this is a hack to include unused props */')
);
return json;
},
},
Expand Down Expand Up @@ -801,8 +797,8 @@ const ANGULAR_NOWRAP_INTERACTIVE_ELEMENT_PLUGIN = () => ({

// extract the props that Wrapper needs
code = code.replaceAll(
'...this.wrapperProps',
'...this.filterPropsThatWrapperNeeds(this.wrapperProps)'
'...this.targetWrapperProps',
'...this.filterPropsThatWrapperNeeds(this.targetWrapperProps)'
);

const ngOnChangesIndex = code.indexOf('ngOnChanges');
Expand Down Expand Up @@ -942,6 +938,21 @@ const QWIK_ONUPDATE_TO_USEVISIBLETASK = () => ({
},
});

/**
* @type {Plugin}
*/
const VUE_FIX_EXTRA_ATTRS_PLUGIN = () => ({
json: {
pre: (json) => {
if (json.name === 'InteractiveElement') {
delete json.children[0].meta.else.bindings.attributes;
}

return json;
},
},
});

/**
* @type {MitosisConfig}
*/
Expand All @@ -967,7 +978,7 @@ module.exports = {
ANGULAR_BIND_THIS_FOR_WINDOW_EVENTS,
ANGULAR_WRAP_SYMBOLS_FETCH_AROUND_CHANGES_DEPS,
ANGULAR_RENAME_NG_ONINIT_TO_NG_AFTERCONTENTINIT_PLUGIN,
ANGULAR_ADD_UNUSED_PROP_TYPES,
REMOVE_UNUSED_PROPS_HACK_PLUGIN,
ANGULAR_NOWRAP_INTERACTIVE_ELEMENT_PLUGIN,
ANGULAR_COMPONENT_REF_UPDATE_TEMPLATE_SSR,
ANGULAR_SKIP_HYDRATION_FOR_CONTENT_COMPONENT,
Expand All @@ -988,6 +999,7 @@ module.exports = {
namePrefix: (path) => (path.includes('/blocks/') ? 'builder' : ''),
cssNamespace: getSeededId,
plugins: [
REMOVE_UNUSED_PROPS_HACK_PLUGIN,
() => ({
json: {
// This plugin handles binding our actions to the `v-on:` Vue syntax:
Expand All @@ -1012,6 +1024,7 @@ module.exports = {
},
},
}),
VUE_FIX_EXTRA_ATTRS_PLUGIN,
],
api: 'options',
asyncComponentImports: false,
Expand Down
18 changes: 17 additions & 1 deletion packages/sdks/src/blocks/image/image.lite.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { Show, useMetadata, useStore } from '@builder.io/mitosis';
import {
Show,
onMount,
useMetadata,
useStore,
useTarget,
} from '@builder.io/mitosis';
import type { JSX } from '@builder.io/mitosis/jsx-runtime';
import { getSrcSet } from './image.helpers.js';
import type { ImageProps } from './image.types.js';
Expand Down Expand Up @@ -66,6 +72,16 @@ export default function Image(props: ImageProps) {
},
});

onMount(() => {
useTarget({
vue: () => {
/** this is a hack to include unused props */
const _ = {
a: props.lazy,
};
},
});
});
return (
<>
<picture>
Expand Down
11 changes: 7 additions & 4 deletions packages/sdks/src/blocks/text/component-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import { TARGET } from '../../constants/target.js';
import type { ComponentInfo } from '../../types/components.js';

export const componentInfo: ComponentInfo = {
shouldReceiveBuilderProps: {
builderBlock: TARGET === 'reactNative' ? true : false,
builderContext: true,
},
shouldReceiveBuilderProps:
TARGET === 'reactNative'
? {
builderBlock: true,
builderContext: true,
}
: {},
name: 'Text',
static: true,
isRSC: true,
Expand Down
2 changes: 1 addition & 1 deletion packages/sdks/src/components/awaiter.lite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default function Awaiter(props: AwaiterProps) {
onMount(() => {
useTarget({
angular: () => {
/** this is a hack to include the input in angular */
/** this is a hack to include unused props */
const _ = {
a: props.load,
b: props.props,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { Show, useMetadata, useStore, type Signal } from '@builder.io/mitosis';
import {
Show,
useMetadata,
useStore,
useTarget,
type Signal,
} from '@builder.io/mitosis';
import type { BuilderContextInterface } from '../../../context/types.js';
import { getBlockActions } from '../../../functions/get-block-actions.js';
import { getBlockProperties } from '../../../functions/get-block-properties.js';
Expand Down Expand Up @@ -49,13 +55,27 @@ export default function InteractiveElement(props: InteractiveElementProps) {
}
: {};
},
get targetWrapperProps() {
return useTarget({
default: props.wrapperProps,
vue: {
...props.wrapperProps,
...(Object.keys(state.attributes).length > 0
? { attributes: state.attributes }
: {}),
},
});
},
});

return (
<Show
when={props.Wrapper.load}
else={
<props.Wrapper {...props.wrapperProps} attributes={state.attributes}>
<props.Wrapper
{...state.targetWrapperProps}
attributes={state.attributes}
>
{props.children}
</props.Wrapper>
}
Expand Down
Loading