Paciolan / remote-component

Dynamically load a React Component from a URL
MIT License
591 stars 49 forks source link

Deep dependencies (auto resolve?) #14

Closed mayteio closed 3 years ago

mayteio commented 3 years ago

Hey there,

Firstly - this package is a godsend. Thanks for building it!

We've got fairly complex components that require deep dependencies, i.e. @material-ui/core/Paper where @material-ui/core is the package.

One solution is to manually type of each of those - though when you bring @material-ui/icons/${iconName} into the mix that can get quickly out of control with 1000+ components.

I noticed that createRequires takes a function - is there a way that I can automatically check which packages my remote component needs and pass in the corresponding package? i.e.

const mui = require('@material-ui/core');

const requires = createRequires({
   '@material-ui/core/Paper': mui.Paper
})

...
joelnet commented 3 years ago

Thanks!

Currently, no.

Because the remote component is executed during run time and those dependencies are resolved during build time it wouldn't be able to.

One scenario to think about is if the app and remote component are built, then the remote component is updated to include an additional dependency. In this scenario, the app would not need to be rebuilt or deployed. So how would it know about the new dependency in the remote component.

It might be possible to create a script to parse the remote component and output a list of dependencies. But even doing this, it's possible that those dependencies would change and the app would be unaware of those new changes.

mayteio commented 3 years ago

Thanks for the quick reply mate. That's true. I've currently got it semi working with a catch-all for every individual component like so:

const requires = createRequires(() => {
  const allMaterial = Object.entries(mui).reduce(
    (accumulator, [key, module]) => ({
      ...accumulator,
      [`@material-ui/core/${key}`]: module,
    }),
    {}
  )
  const allIcons = Object.entries(icons).reduce(
    (accumulator, [key, module]) => ({
      ...accumulator,
      [`@material-ui/icons/${key}`]: module,
    }),
    {}
  )
  const allLab = Object.entries(lab).reduce(
    (accumulator, [key, module]) => ({
      ...accumulator,
      [`@material-ui/lab/${key}`]: module,
    }),
    {}
  )

  return {
    ...resolve,
    ...allMaterial,
    ...allIcons,
    ...allLab,
    "@material-ui/core/styles": require("@material-ui/core").styles,
    "@material-ui/core/utils": require("@material-ui/utils"),
  }
})

Is there a way within the component that you can map top level packages (i.e. @material-ui/core) to their sub module (i.e. @material-ui/core/Paper).

Could this be achieved by stripping the top level and using either dot notation or [] to go deeper into the package?

i.e. @material-ui/core/Paper/something/else -> requires['@material-ui/core'].Paper.something.else?

joelnet commented 3 years ago

Is there a way within the component that you can map top level packages (i.e. @material-ui/core) to their sub module (i.e. @material-ui/core/Paper).

Could this be achieved by stripping the top level and using either dot notation or [] to go deeper into the package?

i.e. @material-ui/core/Paper/something/else -> requires['@material-ui/core'].Paper.something.else?

This is something I would have to play with. But the Manual Configuration section has some details on using a custom requires. The docs on this are light, but it should just be a function that takes a string and returns a module. You can do some custom processing inside that.

I'll be improving the docs on that section shortly. But since it's December, things are slow.

But I can say improvements in this area are on the near horizon 👍

mayteio commented 3 years ago

it should just be a function that takes a string and returns a module.

Thanks mate! That's exactly what I need - though where does this function go? Could you please give a brief code example that I can go off?

Cheers

joelnet commented 3 years ago

The requires should be created in the web application. I haven't tested, but something like this:

const deps = {
  "@material-ui/core": require("@material-ui/core")
};

const requires = name => {
  const match = name.match(/@material-ui\/core\/(?<sub>.+)/, name);

  if (match) {
    return deps["@material-ui/core"][match.groups.sub];
  }

  return deps[name];
}

This should match an import of @material-ui/core/Paper to require("@material-ui/core").Paper

mayteio commented 3 years ago

Awesome, thanks mate!

On Tue, 8 Dec 2020 at 11:16 am, joelnet notifications@github.com wrote:

The requires should be created in the web application. I haven't tested, but something like this:

const deps = { "@material-ui/core": require("@material-ui/core")}; const requires = name => { const match = name.match(/@material-ui\/core\/(?.+)/, name);

if (match) { return deps["@material-ui/core"][match.groups.sub]; }

return deps[name];}

This should match an import of @material-ui/core/Paper to require("@material-ui/core").Paper

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/Paciolan/remote-component/issues/14#issuecomment-740261247, or unsubscribe https://github.com/notifications/unsubscribe-auth/AKPQDNHK6PD3YFG2LPSXT5DSTVV6HANCNFSM4URDTWYA .

joelnet commented 3 years ago

Let me know how it works out. Cheers 🍻

mayteio commented 3 years ago

All sorted - I was already on the money, I had issues with webpack (and it's hella hard to debug code that doesn't exist in your dev environment!)