Skip to content

Commit 844034d

Browse files
Restructuring the RFC to introduce long-term vs compatibility API
1 parent 4c033c4 commit 844034d

File tree

2 files changed

+107
-75
lines changed

2 files changed

+107
-75
lines changed
24.7 KB
Loading

text/1119-ember-api-for-inspector.md

Lines changed: 107 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -50,38 +50,96 @@ The Inspector (on the right) is composed of two main pieces:
5050
- The UI is an Ember app that displays the content of the Inspector window when it runs.
5151
- The folder `ember_debug` is built into a script `ember_debug.js`. The Inspector injects this script into the page to connect to the inspected Ember app.
5252

53+
### Vite support issue
54+
5355
The incompatibility with Vite apps lies in how `ember_debug.js` (on the left) uses ember-source. For a long time, `ember-cli` expressed all the modules using AMD (Asynchronous Module Definition) and `requirejs` `define()` statements. Addons and applications could rely on AMD loading to use these modules. This is what the Inspector does. When using `@embroider/vite` to build the Ember app with Vite, ember-source is loaded as ESM (ECMAScript modules), and there's essentially no `requirejs` module support: the Inspector was designed to work with the AMD approach and breaks when we move to ESM.
5456

5557
In a nutshell, supporting Vite means fixing the bridge between ember-source and `ember_debug.js`.
5658

57-
## Proposed design
59+
### Global approach issue
60+
61+
One important underlying problem with the current architecture is that most of the logic in `ember_debug.js` is the consequence of Ember not providing an API for tools like the Inspector. There's no Ember function that returns _the description of the application_ as a POJO (or JSON or whatever), so the `ember_debug.js` script creates its own description using the AMD modules available, with downsides:
62+
63+
- Since Ember doesn't expose a public API, Ember doesn't have any legitimate responsibility about what modules are available or not. If Ember code is reorganized and a module used by the Inspector has its path changed, there's no signal to tell the Inspector will break: it's the Inspector's problem to be presented with a fait accompli and align with the latest released version of Ember.
64+
65+
- The Inspector's code contains complex conditional pieces to support different Ember versions as the framework internals evolve. What piece of code supports what version is not explicitly indicated and the code is hard to maintain overall. Also, it happens that latest changes in the framework are not reflected, and tests are not always able to catch the issues (e.g. in Ember Inspector 4.13.1, most services are marked as computed properties because the execution path that marks services is no longer taken).
66+
67+
### Conclusion
68+
69+
Fixing the interactions between ember-source and ember-inspector to support Vite requires changes in both ember.js and ember-inspector: the former must expose the modules the inspector need as ESM, the latter must be able to consume ESM modules. However, implementing a new API in ember-source require to think this API future-proof. And future-proof implies that not only we solve the Vite support issue, but also the global approach issue. This is the reason why there are two different aspect to this RFC:
70+
71+
- The long-term goal aspect: Describe the API to implement in ember.js. This API should describe objects a way that match the Inspector requirements.
72+
73+
- The compatibility aspect: Vite support for Ember apps is available from 3.28 to latest. It means that at the time of writing, people who migrated their app to build with Vite are stuck with a non-functional Inspector. We must fix the support for all of these versions, which don't and will never have the new describing objects. In other words, we need a compat API to make the bridge between old Ember versions and Vite world.
74+
75+
## Long-term design
76+
77+
### Overview
78+
79+
// TODO
80+
81+
### Implementation
82+
83+
// TODO
84+
85+
### Explicit import in the Ember app
86+
87+
Using the new `@ember/debug/inspector-support` module in a Vite app requires explicitly importing it:
88+
89+
```js
90+
// my-ember-app/app/app.[js,ts]
91+
92+
import '@ember/debug/inspector-support';
93+
```
94+
95+
This import will become available in the release that includes the implementation presented in the former section. If an Ember app relies on a version of ember-source that doesn't include the new API, the import above should be changed to:
96+
97+
```js
98+
// my-ember-app/app/app.[js,ts]
99+
100+
import '@embroider/compat/inspector-support';
101+
```
102+
103+
The next section will detail the content of `@embroider/compat/inspector-support`.
104+
105+
106+
## Compatibility design
107+
108+
⚠️ Whereas the "Long-term design" section aims at defining the best API possible for the future, the present "Compatibility" section is more time sensitive. As explained earlier, Vite is already there, it should become the default in Ember 6.7, and the Inspector is currently broken for Ember+Vite developers. That's the reason why this section proposes a solution that is easy to implement in a short delay without reinventing entirely the communication between ember-source and ember-inspector.
58109

59110
### Overview
60111

61-
To fix the interaction between ember-source and ember-inspector, we want to implement changes in both repositories:
112+
To fix the interaction between ember-source and ember-inspector for versions that don't have the new API yet, we want to use the concept of "compat" functionnality introduced in Embroider. The package `@embroider/compat` is what makes the bridge between classic Ember and Vite world.
62113

63-
- In **ember.js**: we want to implement an API to expose ESM modules from Ember. The exposed modules are those `ember_debug` needs to send relevant information to the Inspector UI. To do so, we want to introduce a new module `@ember/debug/inspector-support.js` that one can include in their Ember app. This module would define a global (e.g. `emberInspectorLoader`) which provides a function that loads the ESM modules.
114+
- In **@embroider/compat**: we want to implement a compatibility API to expose ESM modules from Ember. The exposed modules are those `ember_debug` - as currently implemented - needs to send relevant information to the Inspector UI. To do so, we want to introduce a new module `@embroider/compat/inspector-support` that one can include in their Ember app. This module would define a global (e.g. `emberInspectorLoader`) which provides a function that loads the ESM modules.
64115

65-
- In **ember-inspector**: we want to implement the ability for `ember_debug` to import all modules from Ember as ESM modules. This should be done without breaking the previous AMD implementation because the Inspector should keep its current ability to inspect Classic apps built with Ember CLI and Broccoli. To do so, we want to use top-level `await` in a conditional block that would be executed when the AMD modules don't exist. Using top-level `await` implies emitting the `ember_debug` script itself as ESM (This constraint has already been partially answered by a recent refactor of the `ember_debug` build, which now relies on Rollup).
116+
- In **ember-inspector**: we want to implement the ability for `ember_debug` to import all the modules from Ember as ESM modules. This should be done without breaking the previous AMD implementation because the Inspector should keep its current ability to inspect Classic apps built with Ember CLI and Broccoli. To do so, we want to use top-level `await` in a conditional block that would be executed when the AMD modules don't exist. Using top-level `await` implies emitting the `ember_debug` script itself as ESM (This constraint has already been partially answered by a recent refactor of the `ember_debug` build, which now relies on Rollup).
66117

67118
### Implementation
68119

69-
On the **ember.js** side, a new module would expose a global providing a `load` function:
120+
On the **@embroider/compat** side, a new module `@embroider/compat/inspector-support` would expose a global providing a `load` function:
70121

71122
```js
72123
globalThis.emberInspectorLoader = {
73124
async load() {
125+
const [
126+
Application,
127+
// Other names...
128+
] = await Promise.all([
129+
import('@ember/application'),
130+
// Other imports...
131+
])
74132
return {
75-
Application: await import('@ember/application'),
133+
Application,
76134
// Other modules...
77135
}
78136
}
79137
}
80138
```
81139

82-
Note that having this global variable in your app will simply define the `load()` function. Modules will load only when the function is executed, and this will occur on the Inspector side (see below). In other words, if it turns out the Inspector requires a few modules that were not yet involved in running the Ember app on the page, they will be loaded only when the developer starts the inspector.
140+
Note that having `emberInspectorLoader` global variable in your app will simply define the `load()` function. Modules will load only when the function is executed, and this will occur on the Inspector side (see below). In other words, if it turns out the Inspector requires a few modules that were not yet involved in running the Ember app on the page, they will be loaded only when the developer starts the inspector.
83141

84-
On the **ember-inspector** side, the file that handles the interactions with ember.js would look like this:
142+
On the **ember-inspector** side, the file that imports the ESM modules would look like this:
85143

86144
```js
87145
// ember_debug/utils/ember.js
@@ -110,7 +168,7 @@ export {
110168

111169
(Note that the approach has been implemented in a proof of concept so we can make sure the Inspector works fine this way. PRs [ember.js#20892](https://github.com/emberjs/ember.js/pull/20892/files) and [ember-inspector#2625](https://github.com/emberjs/ember-inspector/pull/2625/files) allow testing a first iteration of the Vite support when used together.)
112170

113-
### Detailed API
171+
### Detailed compatibility API
114172

115173
The detailed API described below reflects what the Ember Inspector uses.
116174

@@ -119,77 +177,51 @@ The detailed API described below reflects what the Ember Inspector uses.
119177
<summary>Expand to see the detailed API, with the full list of exports:</summary>
120178

121179
```js
122-
globalThis.emberInspectorLoader = {
123-
async load() {
124-
return {
125-
Application: await import('@ember/application'),
126-
ApplicationNamespace: await import('@ember/application/namespace'),
127-
Array: await import('@ember/array'),
128-
ArrayMutable: await import('@ember/array/mutable'),
129-
ArrayProxy: await import('@ember/array/proxy'),
130-
Component: await import('@ember/component'),
131-
ComputedProperty: await import('@ember/object/computed'),
132-
Controller: await import('@ember/controller'),
133-
Debug: await import('@ember/debug'),
134-
EmberDestroyable: await import('@ember/destroyable'),
135-
EmberObject: await import('@ember/object'),
136-
EnumerableMutable: await import('@ember/enumerable/mutable'),
137-
InternalsEnvironment: await import('@ember/-internals/environment'),
138-
InternalsMeta: await import('@ember/-internals/meta'),
139-
InternalsMetal: await import('@ember/-internals/metal'),
140-
InternalsRuntime: await import('@ember/-internals/runtime'),
141-
InternalsUtils: await import('@ember/-internals/utils'),
142-
InternalsViews: await import('@ember/-internals/views'),
143-
Instrumentation: await import('@ember/instrumentation'),
144-
RSVP: await import('rsvp'),
145-
Runloop: await import('@ember/runloop'),
146-
ObjectInternals: await import('@ember/object/internals'),
147-
Service: await import('@ember/service'),
148-
ObjectCore: await import('@ember/object/core'),
149-
ObjectEvented: await import('@ember/object/evented'),
150-
ObjectProxy: await import('@ember/object/proxy'),
151-
ObjectObservable: await import('@ember/object/observable'),
152-
ObjectPromiseProxyMixin: await import('@ember/object/promise-proxy-mixin'),
153-
VERSION: await import('ember/version'),
154-
GlimmerComponent: await import('@glimmer/component'),
155-
GlimmerManager: await import('@glimmer/manager'),
156-
GlimmerReference: await import('@glimmer/reference'),
157-
GlimmerRuntime: await import('@glimmer/runtime'),
158-
GlimmerUtil: await import('@glimmer/util'),
159-
GlimmerValidator: await import('@glimmer/validator'),
160-
}
161-
}
162-
}
180+
Application: await import('@ember/application'),
181+
ApplicationNamespace: await import('@ember/application/namespace'),
182+
Array: await import('@ember/array'),
183+
ArrayMutable: await import('@ember/array/mutable'),
184+
ArrayProxy: await import('@ember/array/proxy'),
185+
Component: await import('@ember/component'),
186+
ComputedProperty: await import('@ember/object/computed'),
187+
Controller: await import('@ember/controller'),
188+
Debug: await import('@ember/debug'),
189+
EmberDestroyable: await import('@ember/destroyable'),
190+
EmberObject: await import('@ember/object'),
191+
EnumerableMutable: await import('@ember/enumerable/mutable'),
192+
InternalsEnvironment: await import('@ember/-internals/environment'),
193+
InternalsMeta: await import('@ember/-internals/meta'),
194+
InternalsMetal: await import('@ember/-internals/metal'),
195+
InternalsRuntime: await import('@ember/-internals/runtime'),
196+
InternalsUtils: await import('@ember/-internals/utils'),
197+
InternalsViews: await import('@ember/-internals/views'),
198+
Instrumentation: await import('@ember/instrumentation'),
199+
RSVP: await import('rsvp'),
200+
Runloop: await import('@ember/runloop'),
201+
ObjectInternals: await import('@ember/object/internals'),
202+
Service: await import('@ember/service'),
203+
ObjectCore: await import('@ember/object/core'),
204+
ObjectEvented: await import('@ember/object/evented'),
205+
ObjectProxy: await import('@ember/object/proxy'),
206+
ObjectObservable: await import('@ember/object/observable'),
207+
ObjectPromiseProxyMixin: await import('@ember/object/promise-proxy-mixin'),
208+
VERSION: await import('ember/version'),
209+
GlimmerComponent: await import('@glimmer/component'),
210+
GlimmerManager: await import('@glimmer/manager'),
211+
GlimmerReference: await import('@glimmer/reference'),
212+
GlimmerRuntime: await import('@glimmer/runtime'),
213+
GlimmerUtil: await import('@glimmer/util'),
214+
GlimmerValidator: await import('@glimmer/validator'),
163215
```
164216

165217
</details>
166218

167-
### Explicit import in the Ember app & polyfill
168-
169-
Using the new `@ember/debug/inspector-support.js` module in a Vite app requires explicitly importing it:
170-
171-
```js
172-
// my-ember-app/app/app.[js,ts]
173-
174-
import '@ember/debug/inspector-support';
175-
```
176-
177-
This import will become available in the release that includes the implementation presented in the former section. However, Vite support for Ember apps is available back to 3.28. It means that people using Vite from 3.28 to whatever version includes this RFC will have a non-functional inspector. We intend to solve this issue using a polyfill implemented in a dedicated package. If an Ember app relies on a version of ember-source that doesn't include the new API, the import above should be changed to:
178-
179-
```js
180-
// my-ember-app/app/app.[js,ts]
181-
182-
import '@embroider/inspector-support-polyfill';
183-
```
184-
185-
The polyfill will be in charge of providing the script that contains the API. Additionally:
186-
- It will adapt the content to the ember-source version if necessary (e.g We identified a module path which is different in version 4.8 and lower)
187-
- It will error if the ember-source version includes the new API, and teach developers they should remove the dependency on the polyfill and replace the import with `'@ember/debug/inspector-support`.
219+
### Ember versions concerns
188220

189-
## Related concerns
221+
Since `'@ember/debug/inspector-support` fits the way the Inspector currently works, the content should fit each supported Ember version depending on modules availability:
190222

191-
### Mixins deprecation
223+
- The content of the script will adapt to the ember-source version relying on `@embroider/macros` (e.g. We identified a module path which is different in version 4.8 and lower).
192224

193-
Mixins are currently being deprecated (see [#1111 to #1117](https://github.com/emberjs/rfcs/pulls?q=is%3Apr+author%3Awagenet+created%3A%3E2025-06-01+)). However, the detailed API above imports mixins. This is because it would be preferable to implement the present RFC before mixins are actually removed. Removing the mixins is a breaking change that would likely be released in Ember 7. On the other hand, Vite could become the default way to build Ember apps from 6.7: having a functional Vite support ready by this time would preserve a good developer experience for people creating new Ember apps.
225+
- This philosophy also applies to changes that could be done in future versions. For instance Mixins are currently being deprecated (see [#1111 to #1117](https://github.com/emberjs/rfcs/pulls?q=is%3Apr+author%3Awagenet+created%3A%3E2025-06-01+)). Removing the mixins is a breaking change that would likely be released in Ember 7. However, the compatibility API above imports mixins because the Inspector currently use them. If we can't get the Long-term API ready for Ember 7, then removing the mixins from the compatibility API and defining a macro condition to have them in 6+ would be part of [#1111 to #1117](https://github.com/emberjs/rfcs/pulls?q=is%3Apr+author%3Awagenet+created%3A%3E2025-06-01+) implementation.
194226

195-
Since mixins currently exist, and since the Inspector relies on them to render the correct information, it appears legit to include the mixins in the API. This way, the inspector keeps working correctly in Ember 6 and lower. Therefore, removing the mixins from the API would be part of [#1111 to #1117](https://github.com/emberjs/rfcs/pulls?q=is%3Apr+author%3Awagenet+created%3A%3E2025-06-01+) implementation. With the mixins gone, the Inspector code may require adjustments to display correclty the new without-mixin objects, but this should be ready for the major version that will remove them.
227+
- An error will be thrown if the ember-source version includes the new API, and teach developers they should remove the dependency on the polyfill and replace the import with `'@ember/debug/inspector-support`.

0 commit comments

Comments
 (0)