-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[META] Embroider Readiness #19099
Comments
One way to ease the cost of transition would be to allow addons to indicate statically which of their arguments corresponds to a static string passed in by the caller of the component As a spitball (where
This would allow addons to indicate that a particular "dynamic invocation" only works if the caller supplies a static string. We should only do this if a lot of dynamic component cases are, in practice, caused by a straight-forward "string argument passed to an addon". |
Idk if this is on topic for this issue, but I've been periodically trying to figure out full staticness over on emberclear, and have been keeping a paper trail of issues and their solutions. So, if people run in to issues, maybe the Abe documentation can help out? Idk Also, I'm super excited for embroider, and already have big plans once full staticness is achieved |
Our application is a dynamic content delivery system. Content creators assemble content out of "Activity Elements", each of which has a corresponding Ember Component, the name of which is defined on an Ember Data model. The heart of this system more or less looks like this: // example model definition
export default class TextElement extends Model {
_componentName = 'text-element';
} {{#each (sort-by "position" @activityElements) as |activityElement|}}
{{component (get activityElement "_componentName")}} My reading of the above suggests that this is a |
What is |
This would be an array of Ember Data Model instances passed into a |
We also invested a bit heavily into dynamic components, FYI. I asked about it here last year: https://discuss.emberjs.com/t/the-perils-of-dynamic-component-invocation/16784. |
So do we. We literally don't know the components that are going to be used in the app in advance. They sit in a database (which of course makes them changeable), are compiled on the backend and are sent on-demand to the frontend. Not being able to use the dynamic |
Could a map be used of all valid components for that scenario? For example: {{#let (hash
Foo=(import 'path/to/foo')
Etc=...
) as |validComponents|
}}
{{component (get validComponents @someDynamicValue)}}
{{/let}} ? Like, you can't truely have full dynamic components, because you can only render what's in your app -- creating a list to choose what to render would also help in debugging, too. "Oh, this value wasn't one of the valid components" |
This would definitely cover our use case. |
@NullVoxPopuli @jherdman Awesome! In general, the way to think about what people would need to do instead of Options include:
Now you might be thinking: how could So let's say you're writing a component that allows you to write Your template for {{#let (hash text=(component "inputs/text-field") checkbox=(component "inputs/checkbox")) as |components|}}
{{component (get components @type)}}
{{/let}} When you write the code this way, Embroider can see that it only needs to include two components in the bundle. Had you written it this way: {{component (concat "inputs/" @type "-field")}} (yes, things like that are surprisingly common) then Embroider cannot easily analyze the code and limit the components that will be included in the bundle. This is even worse if you did the work in JavaScript (which is also very common): export default class extends Component {
get innerComponent() {
return `inputs/${this.args.type}-field`;
}
} with this template: {{component this.innerComponent}} It's a small tweak, but it makes it possible for Embroider to determine which components are actually used. |
I also want to spell out connections to two other in-flight features more carefully.
If we rewrite the previous example using those two features, it looks like: ---
import TextField from "./text-field";
import Checkbox from "./checkbox";
---
{{#let (hash text=TextField checkbox=Checkbox) as |components|}}
{{component (get components @type)}}
{{/let}} This has the nice property of eliminating a special rule that Embroider would have to understand, and making the user model for code splitting even more about modules. Using Component Classes as InvokablesSince RFC #481, a component class has been a fully self-contained unit that has all the information that is needed for the Glimmer VM to invoke it as a component.
Since RFC #432 (contextual helpers and modifiers) and RFC #496 (handlebars strict mode), the design for the future of the Ember's template syntax is: expressions containing helpers, components or modifiers can be invoked as helpers, components or modifiers. The implication of the current collection of approved RFCs is that That said, for clarity, we should create a new RFC that explicitly specifies this behavior. |
Specifying a list of valid components in HBS somewhere is doable for us, but there's a couple caveats worth calling out:
In any case, I feel like dynamic components needs its own issue to discuss rather than taking over this one 🙈 😄 |
I agree, and will open one shortly in the RFC repo, and post a link here. |
@mehulkar @wycats @jherdman what about allowing users to just import their list of dynamically invocable components? Seems much easier to follow than a helper that includes an extra level of indirection.
Then the template can just do: I'd much prefer something like this that is easier search/read/analyze, and easily detect where these components are being used. Thoughts? |
@Samsinite yeah i think that's the idea. The helper in HBS would mostly be syntactic sugar and useful for template-only components. |
@Samsinite that's basically what I proposed as a near-term fix (before template imports). It would require us to allow component classes to be invocable, which is what I was talking about in this comment. Let's go do it! |
Ah, that makes sense for template only components, sorry for mis-understanding :). I also like the syntax for template only components of: ---
import TextField from "./text-field";
import Checkbox from "./checkbox";
---
{{#let (hash text=TextField checkbox=Checkbox) as |components|}}
{{component (get components @type)}}
{{/let}} as well, in-fact, I'd probably use it for template only and js backed components, depending on what others on the team find easier to read. |
Today, Embroider in compatibility mode can be used in new applications and in many existing applications. It is more difficult to use Embroider in
staticComponents
, mode, which is necessary to get the benefit ofsplitAtRoutes
mode.Issue #501 on the Embroider repository tracks the remaining issues needed to stabilize Embroider as a part of an Ember.js release.
This issue tracks the steps that we need to take before people can practically use Ember with Embroider as a supported option with route-based code splitting ("Embroider readiness"). While there are many granular ways to use Embroider (including a compatibility mode that offers few concrete benefits but is important for migrating Ember itself to Embroider by default), this issue is focused on the ability to use Embroider with a normal Ember application and get benefits from route-based code splitting.
Technical Requirements
As described in the Embroider README, in order to enable route-based code splitting (
splitAtRoutes
), an application must be able to enable these flags:staticAddonTestSupportTrees
staticAddonTrees
staticHelpers
staticComponents
If an addon or application cannot work in the presence of these flags, they are using "classic dynamic features".
MVP: Deprecate and replace
(component dynamicString)
For the first target of Embroider readiness ("MVP"), we need to eliminate the most common roadblocks that we've found when trying to enable the static flags in real-world applications.
For the MVP milestone, it is a non-goal for all ecosystem addons support these flags.
Instead, the goal is for applications to have a reasonable transition path to
splitAtRoutes
, and that it's possible to build substantial, non-trivial applications in that mode. That means that all addons included in the default blueprint must have migrated away from classic dynamic features. It also means that addons that are frequently used in real-world applications, such as Ember concurrency, must not use classic dynamic features.staticComponents
This is the most important static flag, and its requirement poses the most substantial obstacle to the MVP target.
In order to enable
staticComponents
, an application (including its addons) must be free of all uses of(component dynamicString)
.Importantly, applications and their addons are allowed to use
(component "static string")
instaticComponents
mode.In practice, this means that we will need to migrate away from patterns like this:
Addons that currently take strings as part of their public API will need to, instead, take components, which would mean that this addon would need to migrate to an approach that required their users to supply the component to invoke, rather than a string.
At minimum, in order to allow addons to migrate away from
(component dynamicString)
, we will need to create a new version of thecomponent
keyword that does not allow invocation of dynamic components.Additionally, we need to fix a bug that inadvertantly allows component invocation with angle-bracket syntax to work the same way as the dynamic
component
keyword. This needs to be done soon, because once people begin to try to migrate away from(component dynamic)
, there's a high likelihood that people will accidentally migrate to<dynamic>
, observe that it works, and move on.Action items:
(component)
that only supports static strings (requires RFC)staticHelpers
The
staticHelpers
flag does not reduce the expressiveness of Ember templates, but it means that the entire list of an application's helpers will not be available in the set of modules in the loader.We assume that this will have breaking effects on Ember applications, but many fewer problems than
staticComponents
. We don't currently expectstaticHelpers
to affect the MVP target, but that may change as we attempt to upgrade applications tosplitAtRoutes
. If that happens, we will need to evaluate the problematic use-cases and consider new APIs to facilitate migration.staticAddonTrees
andstaticAddonTestSupportTrees
The current assumption is that these flags are compatible with most idiomatic Ember apps and addons, and therefore do not pose any substantial problems for the requirements of the MVP target.
Next Steps
This issue is intended to remain as a tracking issue after we achieve the MVP target. After the MVP target, we will need to facilitate a complete ecosystem migration and improve the ergonomics of dynamic use-cases (which retaining support for code splitting). Template imports is likely to help achieve these goals.
The text was updated successfully, but these errors were encountered: