Import React file from the plugin' settings folder

Is it possible to import a react component (.jsx file) from the plugin settings folder and then show it in the main App.js ?

The XD API looks like this:

const fs = require("uxp").storage.localFileSystem;
const folder = await fs.getDataFolder(); // plugin settings folder
const entries = await folder.getEntries("SettingsComponent.js"); // look inside it

but how to insert it in the App.js? Looks like impossible :confused:

This doesnā€™t sound like itā€™s possible. From my understanding of React, it getā€™s compiled to vanilla javascript which will then be executed during runtime. So if you read a file (which happens during runtime), the underlying code isnā€™t really part of the React framework anymore. You could of course manipulate the HTML DOM manually, but I guess that wasnā€™t what you intented.

What are you trying to achieve / what is the specific usecase?

1 Like

Hi @simonhenke I just want to import a React component (a file) that is on the setting folder.

this is how you would regularly import a component inside the App.js:

const Component = require("./components/Component");

but, as you can see the plugin settings folder is not that easy to require as a string would be.

And itā€™s definitely necessary for the file to sit inside the settings folder?
Canā€™t you just have it inside your project structure and import it from there? You could have any other settings file inside the settings folder and read it, then display the component (or not) based on this file or parameter you read.

The problem is that fs.getDataFolder() is a function, so it retrieves the correct path during runtime. Thatā€™s already to late though, your React imports get processed during the compilation/bundling step.

yes, because I receive that code server side. So I save it in a react file (.js) and if I could import it properly I could use it inside the plugin.

Eventually what I want to do is what .innerHTML would do in vanilla javascript, but thatā€™s not allowed in React (meaning is forbidden). So, I am trying to find a way to actually include some code from outside (in this case the settings folder)

Interesting, Iā€™ve never heard of such a use case. Why does the React Component have to come from a server during runtime?

.innerHTML sets the content of an Element, while respecting HTML syntax, doesnā€™t it?
So for example you could add '<p>someText</p>ā€™ to a DIV-Element and it would get added as a child.
A full React Component compiles to more than just HTML-Elements though. There might be instance variables, functions, event callbacks etc.

Iā€™m probably not much of a help, sorry :smiley: Maybe someone else knows more about such a use case.

1 Like

@kerrishotts please help me out :pray::pray::pray:

Maybe it would help to better understand your particular use case.

React generally depends upon bundlers and transpilation, which means itā€™s not going to be able to easily include a file from outside its compilation environment as a component. And then never mind getting everything to be wired up correctly so that events work, state is properly handled, etc.

Iā€™ll note that downloading and executing code off your server is generally not considered a great idea ā€“ there are a whole host of security risks in doing so. While UXP is sandboxed, there are definitely ways beyond accessing the file system that could harm your user should an attacker compromise the code you received from the server.

Iā€™m going to assume a few things at this point, given the name of the component ā€“ Iā€™m guessing this is a list of preferences, and that the list of preferences itself is often modified or generated from elsewhere, and youā€™re trying to re-use it. In this case, Iā€™d suggest a more generic approach, and encode the settings layout as a JSON file that you could then use to re-construct everything in the UI.

It wouldnā€™t be horribly difficult to do so with a reasonably simple React component iterating over the fields in the JSON object and creating UI controls based on whatā€™s inside. In fact, thereā€™s probably a few re-usable React components that might already do that for you.

All of that to say that if thatā€™s your use case, you donā€™t need to download actual code to get the desired functionality.

1 Like

Thanks for the tips, but unfortunately there is more code involved than having a just a few settings written in a json. BTW, itā€™s called ā€œSettingsComponentā€ because it comes from the settings folder (please read it as ā€œComponentFromSettingsFolderā€).


Alright, let me explain what my use-case is:

  1. I need to server-side get a new component (saving it as a file, in the plugin settings folder) and then show it in the plugin interface in XD. The component has html-UI, script logic: functions, handle state and more.

  2. I want to bypass the waiting for the XD team to publish the plugin on the store, because frequent XDā€™s API changes or my possible mistakes and also because I want to have a few of this functionality received via a payment-gate.

  3. The server will be secured via SSL and since UXP is sandboxes, I see a close-to-none risk of someone hacking is any way, shape or form.

  4. It would be no problem if React would allow the javascriptā€™s innerHTML method on a ref but, since itā€™s not allowed to use scripts, I cannot take this approach. The only solutions I see is via components-as-a-file to be rendered in the main.The problem is I donā€™t know how to pick them from that folder.

@kerrishotts Any suggestion is much appreciated :pray:

I have tried react-live which is suppose to render react (https://github.com/FormidableLabs/react-live) but it got this error in the plugin UI:

image

Also tried this approachā€¦ nothing :frowning:

const MyLazyComponent = React.lazy(
  () =>
    new Promise(async (resolve) => {
      const folder = await fs.getDataFolder();
      const entries = await folder.getEntries();
      // entries.forEach((entry) => console.log(entry.name));
      // entries.forEach((entry) => console.log(entry.url));
      // entries.forEach((entry) => console.log(entry.nativePath));
      const file = await entries.find(
        (entry) => entry.name === "ServerCode.js"
      );
      const module = await import(file.nativePath);
      console.log("MODULE", module);
      setTimeout(() => resolve(module), 1000);
    })
);
  return (
    <panel className={styles.panel}>
      <React.Suspense fallback={<div>Loading...</div>}>
        {MyLazyComponent ? <MyLazyComponent /> : null}
      </React.Suspense>
    </panel>
  );

@kerrishotts

EvalError: Code generation from strings disallowed for this context

I wonder why this gets triggered and how to bypass it :flushed:

I also added in webpack.config.js:

  mode: "production",

but no success

Anything that tries to evaluate a string as code (eval, new Function, etc.) is not allowed in the XD plugin context. Thereā€™s no real good way around it.

Iā€™ll note that #2 above (avoiding the review process) is not a great reason to try this ā€“ you may end up on the bad end of things if something fails badly enough and enough people report / negatively review the plugin. Iā€™ve included @ashryan in case he has more to say.

For #3, UXP is sandboxed, yes, but that doesnā€™t mean that an attacker couldnā€™t use social engineering to try and get your user to do something wrong. SSL is also susceptible to man-in-the-middle attacks (especially on captive networks, such as hotels use).

Of course when downloading things remotely, youā€™ll also have to handle the case where thereā€™s no network connection or the file is corrupt. W/ JS that probably means a broken plugin if thereā€™s a failure, unless you catch that appropriately elsewhere.

You might be able to get SSR/hydration to do something of use here, but generally I think itā€™d be way easier to use a DSL of sorts (w/ JSON or the like) than it would be to try and get a plugin to load a remote React componentā€¦

2 Likes