(extracted from #23678)
* Move Wizard back into main app, remove Wizard addon
* Remove Wizard-related resolver or build hacks
* Install and enable `@embroider/router`
* Add "wizard" to `splitAtRoutes`
In a fully optimized Embroider app, route-based code splitting more
or less Just Work™ – install `@embroider/router`, subclass from it,
configure which routes you want to split and that's about it.
However, our app is not "fully optimized", by which I mean we are
not able to turn on all the `static*` flags.
In Embroider, "static" means "statically analyzable". Specifically
it means that all inter-dependencies between modules (files) are
explicitly expressed as `import`s, as opposed to `{{i18n ...}}`
magically means "look for the default export in app/helpers/i18n.js"
or something even more dynamic with the resolver.
Without turning on those flags, Embroider behaves conservatively,
slurps up all `app` files eagerly into the primary bundle/chunks.
So, while you _could_ turn on route-based code splitting, there
won't be much to split.
The commits leading up to this involves a bunch of refactors and
cleanups that 1) works perfectly fine in the classic build, 2) are
good and useful in their own right, but also 3) re-arranged things
such that most dependencies are now explicit.
With those in place, I was able to move all the wizard code into
the "app/static" folder. Embroider does not eagerly pull things from
this folder into any bundle, unless something explicitly "asks" for
them via `imports`. Conversely, things from this folder are not
registered with the resolver and are not added to the `loader.js`
registry.
In conjunction with route-based code splitting, we now have the
ability to split out islands of on-demand functionalities from the
main app bundle.
When you split a route in Embroider, it automatically creates a
bundle/entrypoint with the relevant routes/templates/controllers
matching that route prefix. Anything they import will be added to
the bundle as well, assuming they are not already in the main app
bundle, which is where the "app/static" folder comes into play.
The "app/static" folder name is not special. It is configured in
ember-cli-build.js. Alternatively, we could have left everything
in their normal locations, and add more fine-grained paths to the
`staticAppPaths` array. I just thought it would be easy to manage
and scale, and less error-prone to do it this way.
Note that putting things in `app/static` does not guarantee that
it would not be part of the main app bundle. For example, if we
were to add an `import ... from "app/static/wizard/...";` in a
main bundle file (say, `app.js`), then that chunk of the module
graph would be pulled in. (Consider using `await import(...)`?)
Overtime, we can build better tooling (e.g. lint rules and babel
macros to make things less repetitive) as we expand the use of
this pattern, but this is a start.
Co-authored-by: Godfrey Chan <godfreykfc@gmail.com>
Normally, modules defined under `blah/index` can be imported as `blah`. This is also true of Ember resolver lookups - `<MyComponent />` should resolve to the same as `<MyComponent::Index />`. This was working as expected in Discourse core, but we had not implemented the same in our custom resolver logic for themes/plugins.
This commit implements the `/index` fallback, and adds a test for the behaviour.
Named outlets are deprecated and will be removed in Ember 4.x.
Backwards-compatibility shims are introduced so that plugin overrides to `controller:composer` are ported to `service:composer`.
In the past, the result of template compilation would be stored directly in `Ember.TEMPLATES`. Following the move to more modern ember-cli-based compilation, templates are now compiled to es6 modules. To handle forward/backwards compatibility during these changes we had logic in `discourse-boot` which would extract templates from the es6 modules and store them into the legacy-style `Ember.TEMPLATES` object.
This commit removes that shim, and updates our resolver to fetch templates directly from es6 modules. This is closer to how 'vanilla' Ember handles template resolution. We still have a lot of discourse-specific logic, but now it is centralised in one location and should be easier to understand and normalize in future.
This commit should not introduce any behaviour change.
Ember's default resolver only looks for components/services/etc. which are namespaced under the app's `modulePrefix` (`discourse`, in our case). To use addon components/services/etc., the addon must re-export them in its `app/` directory.
In order to support plugins, our custom resolver does a 'suffix match'. This has an unintended side-effect of matching things which are not part of the discourse app or themes/plugins. We've come to rely on this for a few in-repo addons like `select-kit`, `admin` and `wizard`.
This unrestricted 'suffix matching' can cause some very unexpected behaviour. For example, the ember-inspector browser extension has a module called `ember_debug/service/session`. When looking up `service:session`, our resolver was choosing that third-party service over our own Session service. This means Discourse fails to boot when the Ember Inspector is open.
This commit restricts the 'suffix matching' to a known set of namespaces. This brings us one step closer to the default Ember Resolver implementation, and reduces the chance of unexpected behaviour like the ember-inspector issue.
This commit also updates the `dialog-holder` addon to export its service under the app directory, so that we don't need to account for it in the resolver. We may want to consider doing the same for things like `select-kit` and `truth-helpers`, but is beyond the scope of this commit.
These are in widespread use, and upgrading themes/plugins right now would break their compatibility with the stable branch. These should be unsilenced for the release of 2.9.0 stable.
This will allow consumers to inject it using `topicTrackingState: service()` in preparation for the removal of implicit injections in Ember 4.0. `topic-tracking-state:main` is still available and will print a deprecation notice.
Ideally we would convert topic-tracking-state into a true service, rather than registering a model instance into the registry. However, inter-dependencies between service injections make this very difficult to achieve. We don't want to block Glimmer Component work, so this commit does the minimum for now.
This will allow consumers to inject it using `site: service()` in preparation for the removal of implicit injections in Ember 4.0. `site:main` is still available and will print a deprecation notice.
This will allow consumers to inject it using `session: service()` in preparation for the removal of implicit injections in Ember 4.0. `session:main` is still available and will print a deprecation notice.
This will allow consumers to inject it using `currentUser: service()` in preparation for the removal of implicit injections in Ember 4.0. `current-user:main` is still available and will print a deprecation notice.
Having them all in one place is much easier to reason with. It also means we can handle them without needing 'fake' registrations (which can sometimes cause odd behavior). This commit just moves the deprecation logic - it does not introduce any new deprecations.
Previously, if a non-admin controller did not have a template defined, then the resolver would return an admin template with the same name. This is not the desired behavior, and regressed in fc36ac6cde. However, we *do* want this behavior for components defined in the admin bundle (because admin components are not namespaced).
This was noticed because the non-admin `badges` route was using the `admin/badges` template
This commit fixes the behavior, and adds a tests for these cases.
The default Ember resolver implementation allows this for components. We need the same for connectors (which are essentially components behind-the-scenes)
This switches us to use the modern ember resolver package, and re-implements a number of our custom resolution rules within it. The legacy resolver remains for now, and is used as a fallback if the modern resolver is unable to resolve a package. When this happens, a warning will be printed to the console.
Co-authored-by: Peter Wagenet <peter.wagenet@gmail.com>
da6edc1 introduced the `lookupView` method, which initialized a fresh resolver, and used it to directly look up raw-views (with no caching). This worked well, but was not a clean solution. It required initializing an entirely new resolver, and did not have any caching.
This commit updates the `helperContext` to include access to the registry, and uses it to perform raw-view lookups. As well as re-using the registry, this also means we're making use of the resolver's built-in cache.
I haven't been able to measure any noticeable performance impact from this change, but there is certainly less work being done, so it may be beneficial on older devices.
Co-authored-by: Ayke Halder <rr-it@users.noreply.github.com>
Time spent in the 'find module with suffix' portion of our `customResolve` function were adding up to around 100ms-150ms when booting the app. This time is spread over 150+ calls, so it's not immediately obvious in flamegraphs.
This commit implements a (reversed) [Trie](https://en.wikipedia.org/wiki/Trie) which enables fast suffix-based lookups on a list of strings.
In my tests, this requires < 5ms to initialize, and brings the cumulative 'find module with suffix' time down to `< 5ms`. This corresponds to a ~100ms improvement in LCP metrics in my browser.
The only behavior change is to remove support for module filenames which are **not** dasherized. I haven't found any core/theme/plugin modules which are not dasherized in their filenames.
This is to help with the migration to Ember CLI. In the current running
version of Discourse everything should be the same as before, just with
a few extra files that are not used. However, using Ember CLI this can
be installed as an Ember addon.
Co-Authored-By: Jarek Radosz <jradosz@gmail.com>