BuilderIO / mitosis

Write components once, run everywhere. Compiles to React, Vue, Qwik, Solid, Angular, Svelte, and more.
https://mitosis.builder.io
MIT License
11.86k stars 516 forks source link

Support for custom components in mitosis compiler #1303

Open aminezouari52 opened 7 months ago

aminezouari52 commented 7 months ago

I am interested in helping provide a feature!

Yes

Which generators are impacted?

What problem does this feature solve?

The feature aims to enable Mitosis compiler, to effectively handle custom components. Currently, the compiler struggles for example with lower-cased component names containing dashes, leading to issues during compilation.

Here is an example of the compilation error

example

What does the proposed API look like?

The proposed API enhancements would involve refining the JSX parser within Mitosis to appropriately recognize and handle custom components. This improvement would ensure that when encountered, these components are preserved as-is in the generated output without undergoing any alterations by the Mitosis compiler.

The API might involve modifications or additions to the parsing logic to accurately identify and handle these custom components during compilation.

Additional Information

No response

samijaber commented 7 months ago

There are two things that need to be done:

input:

export default function MyComponent(props) {
  return (
    <my-custom-div></my-custom-div>
  );
}

JSON output:

{
  "@type": "@builder.io/mitosis/component",
  "imports": [],
  "exports": {},
  "inputs": [],
  "meta": {},
  "refs": {},
  "state": {},
  "children": [
    {
      "@type": "@builder.io/mitosis/node",
      "name": "my - custom - div",
      "meta": {},
      "scope": {},
      "properties": {},
      "bindings": {},
      "children": []
    }
  ],
  "context": {
    "get": {},
    "set": {}
  },
  "subComponents": [],
  "name": "MyComponent",
  "hooks": {
    "onMount": [],
    "onEvent": []
  }
}

playground link

This is where we grab the name from the parsed AST:

https://github.com/BuilderIO/mitosis/blob/c43e61f518be5dd8d1c1199661b554934b78ffe6/packages/core/src/parsers/jsx/element-parser.ts#L122

I am unable to see how this introduces spaces in the dash. There might be something else running a transformation on the name fields of Mitosis Nodes before/after this code executes.

Best bet to find the solution for this:

If you are able to take a stab at this, that would be amazing! Otherwise, I will try to whenever I have the time to dedicate to this issue.

rqzheng2015 commented 6 months ago

Hi @samijaber, I have figured out how to support the web-components in mitosis. Please have a look when you have time, thanks.

Tag name issue

The web component tag name has extra spaces after mitosis transforming, for example. swiper-container becomes swiper - container

Reason

I dig in the code, and found out the tag add wrong spaces after codeProcessorfunction execution. image And in deeper levels, it's happening because of the babelTransform code in babel-transform.ts. Babel will consider - as an operator, and automatically add a space before and after it.

image

We could easily reproduce it in babel playground

Fix

We could remove the unnecessary spaces at babelTransformCode function. I have submitted a PR to fix this issue. Please check it here.

rqzheng2015 commented 6 months ago

Type issue

Also, another issue mentiond above, the type issue of web-components in mitosis, I found out it is caused by developers defining jsxImportSource as @builder.io/mitosis in tsconfig.json. The developers can't extend jsx namespace in tsx file, which is necessary to add ts definiton to custom element in mitosis jsx. So, we need to find a way to allow JSX tag type definition extendibility in mitosis.

This is a sample code to allow user to use Swiper in JSX, if we have fixed the type issue, it will work out in mitosis tsx.

declare namespace JSX {
  interface IntrinsicElements {
    'swiper-container': any,
    'swiper-slide': any,
}

export default function Swiper(){
  return <swiper-container slides-per-view="3" speed="500" loop="true" css-mode="true">
    <swiper-slide>Slide 1</swiper-slide>
    <swiper-slide>Slide 2</swiper-slide>
    <swiper-slide>Slide 3</swiper-slide>
  </swiper-container>
}

Further details about Swiper element can be read here.

samijaber commented 6 months ago

Tag name issue:

@rqzheng2015 that's awesome, thanks for investigating! I saw your fix to replace() the spaces: I think it should be localized to this codeProcessor logic:

const result = `node.name.contains('-') ? node.name : codeProcessor('dynamic-jsx-elements', json)(node.name, '');

That's because we only want that codeProcessor to run if the node.name value is dynamic JavaScript. Checking for - here should handle most scenarios.

Type issue

Oh, this is actually already possible! You can extend the runtime by putting this in a .d.ts file:

declare module '@builder.io/mitosis/jsx-runtime' {
  declare namespace JSX {
    interface IntrinsicElements {
      'swiper-container': any;
      'swiper-slide': any;
    }
  }
}

We should document it somewhere for clarity. 🤔

rqzheng2015 commented 6 months ago

Thanks @samijaber! And I'm gonnna test the new solution you provided asap.

rqzheng2015 commented 6 months ago

Hi @samijaber . I've changed the code in codeProcessor like you said, and it successfully solves the jsx tag name space-adding issue.

// plugins/process-code/index.ts
   const result = node.name.includes('-')
        ? node.name
        : codeProcessor('dynamic-jsx-elements', json)(node.name, '');

But now, I met another issue when I set the targets to react or preact. It still throws out the same space-adding issue. I dig in the code, and found out it's happening because of the processBindinng function in processTagReferences in core/src/react/hepers.ts, used by both targets' generators.

image

And in processBinding function ,it called stripStateAndPropsRefsfunction, and deeper, it called replaceStateIdentifier function, which leads to babelTransformExpression, thus, once again, transformed by babel makes it add spaces in tag name again.

image

So, @samijaber do you have a better way to solve this babel transform issue? I'm afraid there might be somewhere also trigger the babel transform like these two functions that I have not found, which will break the tag name display normally. I would love to hear from your advice, thanks again.

rqzheng2015 commented 6 months ago

Hi @samijaber, one more question, to fix type issue, I have added the ts definition like you said above.

declare module '@builder.io/mitosis/jsx-runtime' {
     namespace JSX {
        interface IntrinsicElements {
            'swiper-container': any;
            'swiper-slide': any;
        }
    }
}

But now I am facing two issues, playground can be seen here

  1. Mitosis project popup eslint warning to ts declaration, which says "Mitosis component files should only contain import declarations, the component itself (in a default export), and type declarations (only-default-function-and-imports)".
  2. After generation, the ts declration is missing in the result. image

    How could I have better way to make ts declaration to support web component? Please take a look if you have time, thanks.

samijaber commented 6 months ago

@rqzheng2015 for the types issue: what youre showing is a playground-only issue. Mitosis component files .lite.tsx cannot have anything else at the root other than the component itself and certain hooks like useMetadata.

You should add the declare module {} logic in a separate .d.ts file in your project folder, and it will work then.

samijaber commented 6 months ago

@rqzheng2015 thanks for digging further into this. processTagReferences is another place where we should check for -, and if we find that, then we do not call processBinding.

rqzheng2015 commented 6 months ago

@rqzheng2015 thanks for digging further into this. processTagReferences is another place where we should check for -, and if we find that, then we do not call processBinding.

Thanks, I will fix this soon.

rqzheng2015 commented 6 months ago

Hi @samijaber, I have fixed the issue. Please check the PR here, thanks. https://github.com/BuilderIO/mitosis/pull/1318