Importing host app components in Embroider Addon

Hello,

I’m trying to make my addon Embroider compatible. I’ve got a dynamic component scenario that doesn’t seem to be covered by the Embroider manual

I’m trying to eager load a lot of components from both addon and host app. Currently I’ve got this code:

import Component from '@glimmer/component';
import { importSync } from '@embroider/macros';
import { ensureSafeComponent } from '@embroider/util';

export default class InputWrapperComponent extends Component {
  get inputComponent() {
    let module = importSync(`./inputs/${this.args.type}-input`);
    return ensureSafeComponent(module.default, this);
  }
}

This works fine with components defined in my addon (e.g. my-addon/components/inputs/number-input). But I would like to allow the host app to define its own input types - and then import them (e.g. some-app-using-my-addon/components/inputs/avatar-input). And this doesn’t work. I just get this error: Error: Cannot find module './log-input'.

Is there a way to achieve something like that with Embroider? Or do I have to change my approach and for example require addon users to pass a component instead of type attribute?

Or do I have to change my approach and for example require addon users to pass a component instead of type attribute?

Yes, ideally it would be better to have users pass their actual components to you, rather than just strings with the names of those components. This has the benefit of only including components that will actually get used.

If you need an upgrade path for users, you could give them a way to explicitly pass all their components to you. Your addon is no longer allowed to resolve code out of the app, but the app can still resolve its own code, so you could tell people to do something like this:

// app/app.js
import { registerInputs } from "your-addon";
import { importSync } from '@embroider/macros';
registerInputs(type => importSync(`./inputs/${type}-input`))  

By pasting those three lines into their app, they can avoid needing to refactor all the call sites to pass components instead of strings.

In this example, your-addon would provide the registerInputs function that saves the user-provided callback (probably in a service) and then InputWrapperComponent can call the function when it needs to get a component out.

1 Like