Closed samijaber closed 1 year ago
This is just your bundler configuration. You'd run into the same problem with other native modules as well, for example fs-extra
. I might recommend this if you want to be less hacky.
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const ivm = require('isolated-vm');
There are other ways around it but unless you feel strongly about your module graph integrity and static analyzability then it's probably not worth bothering.
Thank you for the prompt response! I will look into the solution you offered, it does feel a little less hacky 😄.
For full context, I am building several SDKs that export components: for React, React Native, Qwik, Vue, SolidJS, Svelte, NextJS-specific React. Those SDKs are importing isolated-vm
, and I publish them to my end-users.
Therefore I have zero control over the web applications that end up importing my SDKs, and I want whatever solution I come up with to work out-of-the-box without needing any change in my users' web app configuration.
I am going to close this seeing as it is normal behavior. Thanks for the improved workaround.
I facing this issue and cannot get any of the workarounds to work once I deploy on vercel . Is there a solution that worked for anyone ?
@djibomar one solution that partially works (for Nextjs specifically) is to:
eval("require")("isolated-vm")
in your code wherever you need to.import ivm from 'isolated-vm'
statement somewhere in code that is servery-side only. like pages/_document.tsx
for example. This guarantees that isolated-vm
is included in the server-side bundle.NOTE: for Nextjs, you cannot use @laverdet 's suggestion:
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const ivm = require('isolated-vm');
because Nextjs does not allow node:*
imports. See https://github.com/vercel/next.js/issues/60491
@samijaber what would you expect from isolated-vm in a client-rendered React component?
@laverdet I want it to work in the SSR only. Obviously it would break the client render, but I have guarantees in place to make sure the code only executes during SSR.
A very minimal repro of what I'm trying to do is:
import React from "react";
const getBrowserEval = () => new Function("return 1+3")();
const getServerEval = () => {
const ivm = eval("require")("isolated-vm");
const isolate = new ivm.Isolate({ memoryLimit: 128 });
const context = isolate.createContextSync();
const result = context.evalSync("1+3");
return result;
};
const isBrowser = typeof window !== "undefined";
const getEvalResult = isBrowser ? getBrowserEval : getServerEval;
export const MyComponent = () => {
return <div>1 + 3 = {getEvalResult()}</div>;
};
use isolated-vm
on the server, and new Function()
on the client to generate the same part of the React app.
I see. You can do this in Webpack. Not sure how you would do it in Vite but I am sure there is a way. In Webpack you just add resolve: { alias: { "isolated-vm": false } }
to the configuration which would cause that import to be null
when bundling. For extra points use DefinePlugin to overwrite typeof window
with "undefined"
and then Terser will automatically remove the ternary.
I wouldn't recommend using eval
and new Function('return '+'...')
interchangeably since it will be difficult or impossible to make them behave the same in all cases. In general if you find yourself concating user code then something has gone wrong.
If you want to avoid the direct eval
pessimizations in the browser you can make it indirect with (0,eval)('1+2')
(assuming strict mode or esm)
Here is a repro using latest NextJS: https://github.com/samijaber/isolated-vm-import-bug
I have seen this issue in a variety of build tooling and web frameworks, including Webpack/Vite and Qwik/Vue/Nuxt. So it is not restricted to NextJS/React.
running
npm run build
(or dev) gives me this issue:For some reason, using this
require
hack works just fine:https://github.com/samijaber/isolated-vm-import-bug/blob/0b23a981f64c814e837412ce05c1353294438ff7/src/app/page.tsx#L1-L5
I am out of my depth here, and not sure why the
import
doesn't work, but the other approach does. I tried finding other issues reporting this but couldn't. Would appreciate any clarity!