Dynamic import build time template only components

Hello, I’m building a new hbs strategy for ember-svg-jar and wonder if it would be possible to dynamic import template only components?

Hbs strategy

The idea is to create a component which dynamic import template only components created at build time

export default class SvgComponent extends GlimmerComponent {
  constructor() {
    super(...arguments);
    this.loadSvg(this.args.name);
  }

  loadSvg(name){
    if(!CACHE.has(name)) {
        return import(name).then(comp => CACHE.set(name))
    }
  }
}
{{#unless this.isLoading}}
 {{component this.args.name}}
{{/unless}}

Would something like is viable with ember-auto-import?

As long as we’re talking about octane-style co-located templates, being template-only doesn’t really affect this question. Template-only components still preprocess into javascript modules that, from the outside, look indistinguishable from components that have separate JS and HBS.

Today ember-auto-import can only import things from NPM packages that are not ember addons. The reason is that ember-addons have their own idiosyncratic way of getting modules into the build, and trying to auto-import them would be redundant with that. In contrast, embroider does do the thing you want here, which is why it’s designed around the v2 addon format that is designed to be analyzable.

Under embroider, the thing you show would basically work in terms of loading components the first time they’re needed. There’s no need for your own CACHE, because ES modules guarantee only-once evaluation – calling import()a second time doesn’t load or run the code twice. After loading a component, to make it invocable in a template you’d still need to call define (see Load Ember-addons on demand from main application), at least until Ember lets us directly invoke component classes (which I expect is coming soon).

But I don’t think you should always use dynamic import() for every SVG. That adds asynchrony and potentially more network requests when it isn’t truly wanted. If you just compile all SVGs to template-only components, there’s really no step two. People could invoke the components directly, and Embroider would include only the ones that are used, and they will split correctly into multiple bundles if people are splitting up their apps, etc.

A lot of the work svg-jar needs to do today is only needed because of the lack of such a general-purpose, pull-based build system. Once we have that, svg-jar can be much simpler. I think ideally it would only need to be a preprocessor that rewrites SVG files to HBS files, adding ...attributes like in your linked issue.

1 Like

Thanks for the detailed answer @ef4 and your continuos work, looking forward for the upcoming features…

People could invoke the components directly, and Embroider would include only the ones that are used, and they will split correctly into multiple bundles if people are splitting up their apps, etc.

I totally agree and I’m glad that embroider with splitting would practically solve most of the scenarios… as long as the usage is statically analyzable, i.e. <Svg::MyCar /> or using AST at build time <Svg @name="my-car" />

But what if we need to support a dynamic scenario?

AFAIK, unused components will be “trimmed” or tree shacked, so in that case I think a dynamic import is the only way? (unless of course you bundle everything) i.e.

<Svg @name={{this.model.picture}} />

{{component this.args.name}}

But I don’t think you should always use dynamic import() for every SVG

I agree, static usage would be encouraged for splitting, but for this use case I want to kind of “mimic” the DX around the default <img src="my-car.svg" /> behaviour

Thanks again