Closed bgonzalez-thestable closed 3 weeks ago
The issue it that the package itself expects to have a window
or document
to be available in the SSR side. Hydrogen local server uses Cloudflare workerd instance. This is to ensure that the local environment that you have is the same as the one that will be running on production. A cloudflare worker doesn't have access to a window
or document
.
Instead of trying to run the html parser in SSR, run it on client side. I tried this package locally.
import * as parser from 'html-react-parser';
const parse = parser.default;
export default function Homepage() {
const data = useLoaderData<typeof loader>();
const [parsedHtml, setParsedHtml] = useState<ReturnType<typeof parse>>();
useEffect(() => {
setParsedHtml(parse('<p>bar</p>'));
});
return (
<div className="home">
{parsedHtml && <div>{parsedHtml}</div>}
</div>
);
}
Make sure to add the parser package in the ssr.optimizeDeps
in the vite.config.ts
ssr: {
optimizeDeps: {
/**
* Include dependencies here if they throw CJS<>ESM errors.
* For example, for the following error:
*
* > ReferenceError: module is not defined
* > at /Users/.../node_modules/example-dep/index.js:1:1
*
* Include 'example-dep' in the array below.
* @see https://vitejs.dev/config/dep-optimization-options
*/
include: ['html-react-parser'],
},
},
Thanks @wizardlyhel
Moving this to client-side would be problematic because in our case we receive markup or markdown from a CMS response. This would require making the entire page client-side, as individual pieces of the structured data received from the CMS may o may not have HTML.
Additionally, this works on Hydrogen Remix classic (no Vite), and it also works on Vite + Remix (no Hydrogen/Oxygen).
We're currently unable to migrate to Vite + Hydrogen because this is blocking the implementation.
Then I would consider using dompurify + jsdom.
window
object that dompurify needs on the server sideI don't know what other limitations these 2 package can run into when executed on the server side. They could be:
The core of any html sanitizer is that they relies on there is a DOM object that they can parse strings into html nodes and run queries to manipulate the node elements. Instead of writing a DOM object, they just reuse the ones from browser. But often, package like jsdom provides way more than just a simple parse and query dom nodes since these packages are used for unit testing. So these dom packages are really large in size because they try to mimic everything, including downloading scripts, that a browser dom does.
I tried using dompurify as well and it had a similar error, I will try it with jsdom.
That said, I still do not understand why this works as expected on all other SSR frameworks out there, this seems to be a problem specific to Oxygen/Hydrogen. As I mentioned, we currently have sites deployed to production running on Hydrogen + Remix classic and there's no errors.
The server should not be trying to parse these into HTML, I tried using this other plugin https://www.npmjs.com/package/isomorphic-dompurify to no avail. And as described in https://github.com/kkomelin/isomorphic-dompurify/issues/214#issuecomment-1868232246 it looks like Hydrogen is loading the wrong file?
It depends on the hosting environment you are on. Some hosting platform supply a global.windows object and some don't. For example, Vercel and Cloudflare workers (what Oxygen is using) doesn't have a global.windows object. The reason why windows often isn't defined is due to performance.
To be honest, the fact that I have to do this kinda import workaround when using html-react-parser
is already strange.
import * as parser from 'html-react-parser';
const parse = parser.default;
This already tells me that the library export is set up oddly. Most likely the difference between es modules setup and cjs setup (what html-react-parser most likely is using).
Since you are saying that this package "should" work on SSR, the file is there and it's just using the wrong one. Why not look into the option of patching the package so that it returns the correct file? https://www.npmjs.com/package/patch-package
Another option is just copy the entire package content locally into your project and see if that works
That's odd, I'm not sure why on your environment you have to import the package that way, for us to works as a normal import.
For example, a simplified hook that parses HTML:
import parse from 'html-react-parser';
export const useHTMLParser = (html) => {
return typeof html === 'string' ? parse(html) : html;
};
The production environments we have are hosted in Oxygen/Cloudflare and the parser works as expected.
@bgonzalez-thestable
You have to ensure that the html-dom-parser
dependency of html-react-parser
resolves to the server-side version. Below are the required changes to your vite.config.ts
file in order to make this work.
import {createRequire} from 'node:module';
const require = createRequire(import.meta.url);
// This needs to be resolved to the server-side version of html-dom-parser
// since running this on the edge will throw an error.
const htmlDomParserPath = require.resolve(
'html-dom-parser/lib/server/html-to-dom',
);
export default defineConfig({
resolve: {
alias: [
{
find: 'html-dom-parser',
replacement: htmlDomParserPath,
},
],
},
ssr: {
optimizeDeps: {
include: ['html-react-parser'],
},
},
})
@dcodrin wow, you're a savior! This is exactly what I was missing.
Thank you so much!
What is the location of your example repository?
No response
Which package or tool is having this issue?
Hydrogen
What version of that package or tool are you using?
2024.7.4
What version of Remix are you using?
2.10.1
Steps to Reproduce
html-react-parser
as a dependency//...
export function Foo() { const parsedHTML = parse('
bar
'); }