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

Unable to render in Astro #697

Closed
shishkin opened this issue Jan 31, 2023 · 13 comments
Closed

Unable to render in Astro #697

shishkin opened this issue Jan 31, 2023 · 13 comments
Labels
bug Something isn't working stale

Comments

@shishkin
Copy link

Description

I struggle to get the react component working in Astro. I followed the usage guide for Next.js with SSR to make sure to parse the schema on the server and import only the AsyncApiComponentWP component.

Component renders in SSG in dev mode, but fails in production build and fails to hydrate on the client. In client-only mode I see the same error as during SSG hydration on the client side. When hydration fails, component is not interactive (though all content is there with proper styles).

Expected result

Expected it to work.

Actual result

When running SSG production build I see this error from Astro:

 error   Unable to render AsyncApiComponentWP!

  This component likely uses @astrojs/react, @astrojs/preact, @astrojs/solid-js, @astrojs/vue or @astrojs/svelte,
  but Astro encountered an error during server-side rendering.

  Please ensure that AsyncApiComponentWP:
  1. Does not unconditionally access browser-specific globals like `window` or `document`.
     If this is unavoidable, use the `client:only` hydration directive.
  2. Does not conditionally return `null` or `undefined` when rendered on the server.

  If you're still stuck, please open an issue on GitHub or join us at https://astro.build/chat.
  Hint:
    Browser APIs are not available on the server.

    If the code is in a framework component, try to access these objects after rendering using lifecycle methods or use a `client:only` directive to make the component exclusively run on the client.

    See https://docs.astro.build/en/guides/troubleshooting/#document-or-window-is-not-defined for more information.

When running in dev mode I see this error in the browser:

index.mjs?v=1ce97521:4 Uncaught (in promise) SyntaxError: The requested module '/node_modules/node-fetch/node_modules/whatwg-url/lib/public-api.js?v=1ce97521' does not provide an export named 'default' (at index.mjs?v=1ce97521:4:8)

Steps to reproduce

Create Astro project and add this component:

---
import "~/styles/asyncapi.css";
import parser from "@asyncapi/parser";
import { AsyncApiComponentWP } from "@asyncapi/react-component";

const parsed = await parser.parse("...");
const schema = JSON.stringify(parsed.json());
---

<AsyncApiComponentWP
  schema={schema}
  config={{ show: { sidebar: true } }}
  client:load
/>

Troubleshooting

I traced node-fetch dependency and it is used by the @asyncapi/parser. I'm not sure why parser is still loaded in AsyncApiComponentWP. I supposed WP stood for "without parser" 🤷

Is there a way to cleanup ESM dependencies for the standalone WP component to really not depend on CJS and node-only or browser-only packages?

Previously I struggled with this component on Deno (ultimately I gave up in favor of a custom element web component option) where the issue was the isomorphic-dompurify. I was wondering why sanitization has to happen inside AsyncApiComponentWP. Maybe the parser should be sanitizing the markup bits of schema, so that parsed schema can be assumed safe?

@magicmatatjahu
Copy link
Member

magicmatatjahu commented Feb 1, 2023

@shishkin Thanks for the issue. Probably Astro "see" the normal AsyncAPI component (with parser onboard) and try to "compile" it - I have similar problem as I remember with older version of NextJS and treeshaking. Try to import component without parser from absolute path:

import AsyncApiComponentWP from "@asyncapi/react-component/esm/containers/AsyncApi/Standalone";

and let me know about problems!

@magicmatatjahu
Copy link
Member

magicmatatjahu commented Feb 1, 2023

Also, you should use stringify (https://github.com/asyncapi/parser-js#stringify) function to stringify the parsed document. Document can have circular references in schemas and normal JSON.stringify can throw error about cyclic values. stringify function exposed by parser preserves that circular references and add another optimizations. React component then can read that, no worry about that :)

So your code should look like:

---
import "~/styles/asyncapi.css";
import parser, { AsyncAPIDocument } from "@asyncapi/parser";
import { AsyncApiComponentWP } from "@asyncapi/react-component/esm/containers/AsyncApi/Standalone";

const parsed = await parser.parse("...");
const schema = AsyncAPIDocument.stringify(parsed);
---

<AsyncApiComponentWP
  schema={schema}
  config={{ show: { sidebar: true } }}
  client:load
/>

@shishkin
Copy link
Author

shishkin commented Feb 1, 2023

Thanks @magicmatatjahu!

Importing import AsyncApiComponent from "@asyncapi/react-component/esm/containers/AsyncApi/Standalone" threw "Cannot find module". Adding lib to the import path @asyncapi/react-component/lib/esm/containers/AsyncApi/Standalone threw "Cannot read properties of undefined (reading 'default')". Also VS Code complains about absence of typings for the module. I tried to hack the component's package.json by adding explicit exports without any success however. Anything else I should try?

@magicmatatjahu
Copy link
Member

@shishkin What version of astro are you using, v1 or latest v2?

@shishkin
Copy link
Author

shishkin commented Feb 1, 2023

Latest 2.0.4

@magicmatatjahu
Copy link
Member

@shishkin You're right, something is wrong with ESM support in Astro. CJS imports /lib/cjs/... work in dev, but don't in build time. I tried to make workaround with this:

  • render component to string in server side
  • run hydration in browser side
---
import { createElement } from "react";
import { renderToString } from "react-dom/server";

import Layout from '../layouts/Layout.astro';
import parser from "@asyncapi/parser";
import { AsyncApiComponentWP } from "@asyncapi/react-component";

const schema = `
asyncapi: '2.5.0'
info:
  title: Account Service
  version: 1.0.0
  description: This service is in charge of processing user signups
channels:
  user/signedup:
    subscribe:
      message:
        $ref: '#/components/messages/UserSignedUp'
components:
  messages:
    UserSignedUp:
      payload:
        type: object
        properties:
          displayName:
            type: string
            description: Name of the user
          email:
            type: string
            format: email
            description: Email of the user
`

const parsed = await parser.parse(schema);
const stringified = parsed.constructor.stringify(parsed);
const config = { show: { sidebar: true } };

const component = createElement(AsyncApiComponentWP, { schema: stringified, config });
const renderedComponent = renderToString(component);
---

<Layout>
  <div id="asyncapi" set:html={renderedComponent}></div>
</Layout>

<script is:inline src="https://unpkg.com/@asyncapi/react-component@1.0.0-next.46/browser/standalone/index.js"></script>
<script define:vars={{ schema: stringified, config }}>
  const root = document.getElementById("asyncapi");
  AsyncApiStandalone.hydrate({ schema, config }, root);  
</script>

And it works, however it's not perfect because you need to fetch whole ReactDOM library in browser. Let me know if you find something better!

@shishkin
Copy link
Author

shishkin commented Feb 3, 2023

Thanks for your suggestion @magicmatatjahu. I somewhat improved on it:

---
import "~/styles/asyncapi.css";
import js from "@asyncapi/react-component/browser/standalone/without-parser.js?url";
import parser from "@asyncapi/parser";
import { AsyncApiComponentWP, ConfigInterface } from "@asyncapi/react-component";

const parsed = await parser.parse(`...`);
const schema = parser.AsyncAPIDocument.stringify(parsed);
const config: ConfigInterface = { show: { sidebar: true } };
---

{
  import.meta.env.SSR ? (
    <>
      <div id="asyncapi">
        <AsyncApiComponentWP schema={schema} config={config} />
      </div>
      <script src={js}></script>
      <script define:vars={{ schema, config }}>
        AsyncApiStandalone.hydrate(
          {schema: schema, config: config},
          document.querySelector("#asyncapi > section") );
      </script>
    </>
  ) : (
    <></>
  )
}

I was able to use normal rendering mechanism without explicit renderToString by guarding with SSR env. I also figured I could import browser build without parser on the client.

Now, if I could only externalize react like esm.sh does and replace it with preact, I could further slim down the JS part of the component hydration.

@magicmatatjahu
Copy link
Member

@shishkin Yeah, sorry for using the standalone build with parser, but as I see you figure out that we also have without parser.

Now, if I could only externalize react like esm.sh does and replace it with preact, I could further slim down the JS part of the component hydration.

yeah, it will be perfect to decrease final size of app. Let me know about any other problems!

@magicmatatjahu magicmatatjahu added the bug Something isn't working label Feb 12, 2023
@sibelius
Copy link

this is breaking on docusaurus as well

@sibelius
Copy link

  • docusaurus errors

  • Error: Cannot read properties of undefined (reading '0')
    Uncaught TypeError: setClickedItem is not a function
    at handleSetExpanded (Toggle.js?f463:16:1)

    constate] Component not wrapped within a Provider.
    at Toggle (webpack-internal:///./node_modules/@asyncapi/react-component/lib/components/Toggle.js:14:21)
    at section
    at ErrorComponent (webpack-internal:///./node_modules/@asyncapi/react-component/lib/containers/Error/Error.js:33:20)
    at AsyncApiComponent

    [Autocomplete] You provided the render option but did not provide a renderer.render. Since v1.6.0, you can provide a render function directly in renderer.
    To get rid of this warning, do any of the following depending on your use case.

  • If you are using the render option only to override Autocomplete's default render function, pass the render function into renderer and remove the render option.

  • If you are using the render option to customize the layout, pass your render function into renderer and use it from the provided parameters of the render option.

  • If you are using the render option to work with React 18, pass an empty render function into renderer.
    See https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-js/autocomplete/#param-render
    at SearchBar

@github-actions
Copy link

This issue has been automatically marked as stale because it has not had recent activity 😴

It will be closed in 120 days if no further activity occurs. To unstale this issue, add a comment with a detailed explanation.

There can be many reasons why some specific issue has no activity. The most probable cause is lack of time, not lack of interest. AsyncAPI Initiative is a Linux Foundation project not owned by a single for-profit company. It is a community-driven initiative ruled under open governance model.

Let us figure out together how to push this issue forward. Connect with us through one of many communication channels we established here.

Thank you for your patience ❤️

@github-actions github-actions bot added the stale label Jun 24, 2023
@sibelius
Copy link

Still a problem on docusuarus

@github-actions github-actions bot removed the stale label Jun 25, 2023
@github-actions
Copy link

This issue has been automatically marked as stale because it has not had recent activity 😴

It will be closed in 120 days if no further activity occurs. To unstale this issue, add a comment with a detailed explanation.

There can be many reasons why some specific issue has no activity. The most probable cause is lack of time, not lack of interest. AsyncAPI Initiative is a Linux Foundation project not owned by a single for-profit company. It is a community-driven initiative ruled under open governance model.

Let us figure out together how to push this issue forward. Connect with us through one of many communication channels we established here.

Thank you for your patience ❤️

@github-actions github-actions bot added the stale label Oct 24, 2023
@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Feb 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working stale
Projects
None yet
Development

No branches or pull requests

3 participants