mitosis icon indicating copy to clipboard operation
mitosis copied to clipboard

Support for custom components in mitosis compiler

Open aminezouari52 opened this issue 1 year ago • 11 comments

I am interested in helping provide a feature!

Yes

Which generators are impacted?

  • [ ] All
  • [ ] Angular
  • [ ] HTML
  • [ ] Qwik
  • [ ] React
  • [ ] React-Native
  • [ ] Solid
  • [ ] Stencil
  • [ ] Svelte
  • [ ] Vue
  • [ ] Web components

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

aminezouari52 avatar Nov 22 '23 10:11 aminezouari52

There are two things that need to be done:

  • Types: I am unsure how to handle this, but the JSX types need to be improved to allow custom components. Or at least, allow a way for the JSX runtime types to be augmented such that the user can manually include their custom components.
  • Parsing: it looks like the main issue is that the JSX parser adds spaces to the component name when it has dashes:

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:

  • create a unit test using the simple mitosis component in this comment (see https://github.com/BuilderIO/mitosis/blob/main/developer/README.md for instructions)
  • generate the test snapshots, and check if the new snapshot for this test in https://github.com/BuilderIO/mitosis/blob/main/packages/core/src/tests/snapshots/parse-jsx.test.ts.snap has those spaces or not.
  • use this unit test to find the root cause of the problem

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.

samijaber avatar Nov 22 '23 20:11 samijaber

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 avatar Dec 10 '23 15:12 rqzheng2015

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.

rqzheng2015 avatar Dec 10 '23 16:12 rqzheng2015

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. 🤔

samijaber avatar Dec 11 '23 15:12 samijaber

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

rqzheng2015 avatar Dec 12 '23 03:12 rqzheng2015

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 avatar Dec 12 '23 14:12 rqzheng2015

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.

rqzheng2015 avatar Dec 13 '23 06:12 rqzheng2015

@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 avatar Dec 14 '23 15:12 samijaber

@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.

samijaber avatar Dec 14 '23 15:12 samijaber

@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 avatar Dec 19 '23 13:12 rqzheng2015

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

rqzheng2015 avatar Dec 21 '23 07:12 rqzheng2015