Skip to content

Commit f4be99d

Browse files
NullVoxPopuliclaude
andcommitted
Drop isInteractive and hasDOM; remove FastBoot codepaths
Since we always have a DOM and modifiers always run, the entire isInteractive/hasDOM split is dead weight. Tear it out. Glimmer-vm: - @glimmer/runtime/lib/environment.ts: drop the isInteractive field on EnvironmentImpl and EnvironmentDelegate. scheduleInstallModifier and scheduleUpdateModifier always schedule. - @glimmer/runtime/lib/compiled/opcodes/dom.ts: drop the early-returns in VM_MODIFIER_OP and VM_DYNAMIC_MODIFIER_OP. Modifiers always run. - @glimmer/interfaces: drop isInteractive from the Environment interface. Ember component layer: - curly + root component managers: lifecycle hooks (willRender, willInsertElement, didInsertElement, willUpdate, didUpdate, didRender, willDestroyElement, willClearRender) always fire. - ComponentStateBucket no longer carries isInteractive. - BootEnvironment loses both fields. Renderer: - BaseRenderer / Renderer / renderComponent no longer take isInteractive or hasDOM env config. - Renderer.getElement always returns the element (no more "not allowed in non-interactive environments" throw). - Renderer.remove always triggers didDestroyElement. - Drop the _isInteractive getter and the debug.isInteractive field. - EmberEnvironmentDelegate has no constructor args. Application/engine boot: - BootOptions drops isInteractive and isBrowser. - _BootOptions no longer threads isInteractive through isBrowser / shouldRender / option override; just _renderMode, location, shouldRender, document, rootElement. - ApplicationInstance always calls setupEventDispatcher. - EngineInstance's singleton list always includes event_dispatcher:main. - EventDispatcher.setup drops the "should never be setup in fastboot" assertion. - @ember/-internals/views/lib/system/event_dispatcher.ts no longer imports from @ember/-internals/glimmer. Component: - The DEBUG tagless event-handler check no longer needs renderer._isInteractive. - _dispatcher always looks up event_dispatcher:main. - appendTo always uses document.querySelector for string selectors; the "no DOM" branch is gone. matches() drops its fastboot guard. @ember/-internals/browser-environment package: deleted entirely. - isChrome/isFirefox were the only remaining consumers (in @ember/debug); inlined there with `typeof window !== 'undefined'` guards. - self/location/history/window references were the only hasDOM users. - Removed from @ember/-internals package exports, root package.json embedded-package map, types/stable manifest, and lib/index.js implicit-modules list. - Test files that imported `window`/`isChrome`/`isFirefox` from browser-environment now use globals directly. <Input> lazily initializes its INPUT_ELEMENT probe (instead of at module -load time) so node-side build tooling that imports the package without a DOM in scope still loads. The DOM is required at render time, which is the contract. Tests: - Drop the non-interactive variants of the lifecycle, custom-modifier, and on-modifier integration tests. Drop the lifecycle test's isInteractive/nonInteractive split — assertHooks always uses the interactive expectations. - Drop the visit_test that checked event_dispatcher:main wasn't created for isInteractive: false. - OutletView render-queue test now appends to a real DOM element rather than a CSS-selector string. - type-tests for BootOptions no longer mention isBrowser. Smoke tests: - Delete app-boot, component-rendering, fastboot-sandbox, and visit tests in node-template, plus their setup-app/setup-component/ build-owner/assert-html-matches helpers. They were FastBoot-only — every one of them used SimpleDOM as a stand-in for document. tests/docs/expected.js: drop the isBrowser, isInteractive, and matches entries (the public surfaces that have been removed). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
1 parent 8c2698d commit f4be99d

45 files changed

Lines changed: 161 additions & 1767 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

lib/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ const shim = addonV1Shim(path.join(__dirname, '..'), {
2727
return {
2828
...meta,
2929
'implicit-modules': [
30-
'./dist/dev/packages/@ember/-internals/browser-environment/index.js',
3130
'./dist/dev/packages/@ember/-internals/container/index.js',
3231
'./dist/dev/packages/@ember/-internals/deprecations/index.js',
3332
'./dist/dev/packages/@ember/-internals/environment/index.js',

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,6 @@
171171
"type": "addon",
172172
"version": 2,
173173
"renamed-modules": {
174-
"@ember/-internals/browser-environment/index.js": "ember-source/@ember/-internals/browser-environment/index.js",
175174
"@ember/-internals/container/index.js": "ember-source/@ember/-internals/container/index.js",
176175
"@ember/-internals/deprecations/index.js": "ember-source/@ember/-internals/deprecations/index.js",
177176
"@ember/-internals/environment/index.js": "ember-source/@ember/-internals/environment/index.js",

packages/@ember/-internals/browser-environment/index.ts

Lines changed: 0 additions & 12 deletions
This file was deleted.

packages/@ember/-internals/browser-environment/lib/has-dom.ts

Lines changed: 0 additions & 19 deletions
This file was deleted.

packages/@ember/-internals/glimmer/lib/component-managers/curly.ts

Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ export default class CurlyComponentManager
251251
owner: Owner,
252252
ComponentClass: ComponentFactory,
253253
args: VMArguments,
254-
{ isInteractive }: Environment,
254+
_env: Environment,
255255
dynamicScope: DynamicScope,
256256
callerSelfRef: Reference
257257
): ComponentStateBucket {
@@ -313,15 +313,9 @@ export default class CurlyComponentManager
313313

314314
// We usually do this in the `didCreateElement`, but that hook doesn't fire for tagless components
315315
if (!hasWrappedElement) {
316-
if (isInteractive) {
317-
component.trigger('willRender');
318-
}
319-
316+
component.trigger('willRender');
320317
component._transitionTo('hasElement');
321-
322-
if (isInteractive) {
323-
component.trigger('willInsertElement');
324-
}
318+
component.trigger('willInsertElement');
325319
}
326320

327321
// Track additional lifecycle metadata about this component in a state bucket.
@@ -331,8 +325,7 @@ export default class CurlyComponentManager
331325
capturedArgs,
332326
argsTag,
333327
finalizer,
334-
hasWrappedElement,
335-
isInteractive
328+
hasWrappedElement
336329
);
337330

338331
if (args.named.has('class')) {
@@ -343,7 +336,7 @@ export default class CurlyComponentManager
343336
processComponentInitializationAssertions(component, props);
344337
}
345338

346-
if (isInteractive && hasWrappedElement) {
339+
if (hasWrappedElement) {
347340
component.trigger('willRender');
348341
}
349342

@@ -367,7 +360,7 @@ export default class CurlyComponentManager
367360
}
368361

369362
didCreateElement(
370-
{ component, classRef, isInteractive, rootRef }: ComponentStateBucket,
363+
{ component, classRef, rootRef }: ComponentStateBucket,
371364
element: Element,
372365
operations: ElementOperations
373366
): void {
@@ -407,28 +400,24 @@ export default class CurlyComponentManager
407400

408401
component._transitionTo('hasElement');
409402

410-
if (isInteractive) {
411-
beginUntrackFrame();
412-
component.trigger('willInsertElement');
413-
endUntrackFrame();
414-
}
403+
beginUntrackFrame();
404+
component.trigger('willInsertElement');
405+
endUntrackFrame();
415406
}
416407

417408
didRenderLayout(bucket: ComponentStateBucket, bounds: Bounds): void {
418409
bucket.component[BOUNDS] = bounds;
419410
bucket.finalize();
420411
}
421412

422-
didCreate({ component, isInteractive }: ComponentStateBucket): void {
423-
if (isInteractive) {
424-
component._transitionTo('inDOM');
425-
component.trigger('didInsertElement');
426-
component.trigger('didRender');
427-
}
413+
didCreate({ component }: ComponentStateBucket): void {
414+
component._transitionTo('inDOM');
415+
component.trigger('didInsertElement');
416+
component.trigger('didRender');
428417
}
429418

430419
update(bucket: ComponentStateBucket): void {
431-
let { component, args, argsTag, argsRevision, isInteractive } = bucket;
420+
let { component, args, argsTag, argsRevision } = bucket;
432421

433422
bucket.finalizer = _instrumentStart('render.component', rerenderInstrumentDetails, component);
434423

@@ -449,10 +438,8 @@ export default class CurlyComponentManager
449438
component.trigger('didReceiveAttrs');
450439
}
451440

452-
if (isInteractive) {
453-
component.trigger('willUpdate');
454-
component.trigger('willRender');
455-
}
441+
component.trigger('willUpdate');
442+
component.trigger('willRender');
456443

457444
endUntrackFrame();
458445

@@ -464,11 +451,9 @@ export default class CurlyComponentManager
464451
bucket.finalize();
465452
}
466453

467-
didUpdate({ component, isInteractive }: ComponentStateBucket): void {
468-
if (isInteractive) {
469-
component.trigger('didUpdate');
470-
component.trigger('didRender');
471-
}
454+
didUpdate({ component }: ComponentStateBucket): void {
455+
component.trigger('didUpdate');
456+
component.trigger('didRender');
472457
}
473458

474459
getDestroyable(bucket: ComponentStateBucket): Nullable<Destroyable> {

packages/@ember/-internals/glimmer/lib/component-managers/root.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class RootComponentManager extends CurlyComponentManager {
3333
_owner: Owner,
3434
_state: unknown,
3535
_args: Nullable<VMArguments>,
36-
{ isInteractive }: Environment,
36+
_env: Environment,
3737
dynamicScope: DynamicScope
3838
) {
3939
let component = this.component;
@@ -46,15 +46,9 @@ class RootComponentManager extends CurlyComponentManager {
4646

4747
// We usually do this in the `didCreateElement`, but that hook doesn't fire for tagless components
4848
if (!hasWrappedElement) {
49-
if (isInteractive) {
50-
component.trigger('willRender');
51-
}
52-
49+
component.trigger('willRender');
5350
component._transitionTo('hasElement');
54-
55-
if (isInteractive) {
56-
component.trigger('willInsertElement');
57-
}
51+
component.trigger('willInsertElement');
5852
}
5953

6054
if (DEBUG) {
@@ -66,8 +60,7 @@ class RootComponentManager extends CurlyComponentManager {
6660
null,
6761
CONSTANT_TAG,
6862
finalizer,
69-
hasWrappedElement,
70-
isInteractive
63+
hasWrappedElement
7164
);
7265

7366
consumeTag(component[DIRTY_TAG]);

packages/@ember/-internals/glimmer/lib/component.ts

Lines changed: 25 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
import { guidFor } from '@ember/-internals/utils';
2121
import { assert } from '@ember/debug';
2222
import { DEBUG } from '@glimmer/env';
23-
import type { Environment, Template, TemplateFactory } from '@glimmer/interfaces';
23+
import type { Template, TemplateFactory } from '@glimmer/interfaces';
2424
import { setInternalComponentManager } from '@glimmer/manager';
2525
import { isUpdatableRef, updateRef } from '@glimmer/reference';
2626
import { normalizeProperty } from '@glimmer/runtime';
@@ -34,27 +34,14 @@ import {
3434
IS_DISPATCHING_ATTRS,
3535
getComponentCapturedArgs,
3636
} from './component-managers/curly';
37-
import { hasDOM } from '@ember/-internals/browser-environment';
3837

3938
// Keep track of which component classes have already been processed for lazy event setup.
4039
let lazyEventsProcessed = new WeakMap<EventDispatcher, WeakSet<object>>();
4140

4241
const EMPTY_ARRAY = Object.freeze([]);
4342

44-
/**
45-
Determines if the element matches the specified selector.
46-
47-
@private
48-
@method matches
49-
@param {DOMElement} el
50-
@param {String} selector
51-
*/
52-
const elMatches: typeof Element.prototype.matches | undefined =
53-
typeof Element !== 'undefined' ? Element.prototype.matches : undefined;
54-
5543
function matches(el: Element, selector: string): boolean {
56-
assert('cannot call `matches` in fastboot mode', elMatches !== undefined);
57-
return elMatches.call(el, selector);
44+
return Element.prototype.matches.call(el, selector);
5845
}
5946

6047
/**
@@ -939,7 +926,7 @@ class Component<S = unknown>
939926
}
940927
}
941928

942-
if (DEBUG && eventDispatcher && this.renderer._isInteractive && this.tagName === '') {
929+
if (DEBUG && eventDispatcher && this.tagName === '') {
943930
let eventNames = [];
944931
let events = eventDispatcher.finalEventNameMapping;
945932

@@ -997,17 +984,9 @@ class Component<S = unknown>
997984
let owner = getOwner(this);
998985
assert('Component is unexpectedly missing an owner', owner);
999986

1000-
if ((owner.lookup('-environment:main') as Environment)!.isInteractive) {
1001-
let dispatcher = owner.lookup('event_dispatcher:main');
1002-
assert(
1003-
'Expected dispatcher to be an EventDispatcher',
1004-
dispatcher instanceof EventDispatcher
1005-
);
1006-
this.__dispatcher = dispatcher;
1007-
} else {
1008-
// In FastBoot we have no EventDispatcher. Set to null to not try again to look it up.
1009-
this.__dispatcher = null;
1010-
}
987+
let dispatcher = owner.lookup('event_dispatcher:main');
988+
assert('Expected dispatcher to be an EventDispatcher', dispatcher instanceof EventDispatcher);
989+
this.__dispatcher = dispatcher;
1011990
}
1012991

1013992
return this.__dispatcher;
@@ -1445,45 +1424,30 @@ class Component<S = unknown>
14451424
@private
14461425
*/
14471426
appendTo(selector: string | Element | SimpleElement) {
1448-
let target;
1449-
1450-
if (hasDOM) {
1451-
assert(
1452-
`Expected a selector or instance of Element`,
1453-
typeof selector === 'string' || selector instanceof Element
1454-
);
1427+
assert(
1428+
`Expected a selector or instance of Element`,
1429+
typeof selector === 'string' || selector instanceof Element
1430+
);
14551431

1456-
target = typeof selector === 'string' ? document.querySelector(selector) : selector;
1432+
let target = typeof selector === 'string' ? document.querySelector(selector) : selector;
14571433

1458-
assert(`You tried to append to (${selector}) but that isn't in the DOM`, target);
1459-
assert('You cannot append to an existing Ember.View.', !matches(target, '.ember-view'));
1460-
assert(
1461-
'You cannot append to an existing Ember.View.',
1462-
(() => {
1463-
let node = target.parentNode;
1464-
while (node instanceof Element) {
1465-
if (matches(node, '.ember-view')) {
1466-
return false;
1467-
}
1468-
1469-
node = node.parentNode;
1434+
assert(`You tried to append to (${selector}) but that isn't in the DOM`, target);
1435+
assert('You cannot append to an existing Ember.View.', !matches(target, '.ember-view'));
1436+
assert(
1437+
'You cannot append to an existing Ember.View.',
1438+
(() => {
1439+
let node = target.parentNode;
1440+
while (node instanceof Element) {
1441+
if (matches(node, '.ember-view')) {
1442+
return false;
14701443
}
14711444

1472-
return true;
1473-
})()
1474-
);
1475-
} else {
1476-
target = selector;
1445+
node = node.parentNode;
1446+
}
14771447

1478-
assert(
1479-
`You tried to append to a selector string (${selector}) in an environment without a DOM`,
1480-
typeof target !== 'string'
1481-
);
1482-
assert(
1483-
`You tried to append to a non-Element (${selector}) in an environment without a DOM`,
1484-
typeof target.appendChild === 'function'
1485-
);
1486-
}
1448+
return true;
1449+
})()
1450+
);
14871451

14881452
// SAFETY: SimpleElement is supposed to be a subset of Element so this _should_ be safe.
14891453
// However, the types are more specific in some places which necessitates the `as`.

0 commit comments

Comments
 (0)