ember-source 6.3.0-alpha.2 → 6.3.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/build-metadata.json +3 -3
  2. package/dist/ember-template-compiler.js +2 -2
  3. package/dist/ember-testing.js +1 -1
  4. package/dist/ember.debug.js +305 -71
  5. package/dist/ember.prod.js +258 -69
  6. package/dist/packages/@ember/-internals/glimmer/index.js +2 -2
  7. package/dist/packages/@ember/application/index.js +2 -2
  8. package/dist/packages/@ember/application/instance.js +1 -1
  9. package/dist/packages/@ember/component/helper.js +1 -1
  10. package/dist/packages/@ember/component/index.js +1 -1
  11. package/dist/packages/@ember/engine/index.js +2 -2
  12. package/dist/packages/@ember/helper/index.js +1 -1
  13. package/dist/packages/@ember/modifier/index.js +1 -1
  14. package/dist/packages/@ember/renderer/index.js +1 -1
  15. package/dist/packages/@ember/routing/index.js +1 -1
  16. package/dist/packages/@ember/routing/route.js +46 -4
  17. package/dist/packages/@ember/template/index.js +1 -1
  18. package/dist/packages/ember/barrel.js +1 -1
  19. package/dist/packages/ember/version.js +1 -1
  20. package/dist/packages/ember-testing/lib/initializers.js +1 -1
  21. package/dist/packages/shared-chunks/{index-C7oKNWrX.js → index-4KqgXTKl.js} +256 -64
  22. package/dist/packages/shared-chunks/{setup-registry-CFRLjlg1.js → setup-registry-ziuiqoKc.js} +1 -1
  23. package/docs/data.json +94 -76
  24. package/package.json +2 -2
  25. package/types/stable/@ember/-internals/glimmer/lib/component-managers/outlet.d.ts +14 -21
  26. package/types/stable/@ember/-internals/glimmer/lib/component-managers/route-template.d.ts +78 -0
  27. package/types/stable/@ember/-internals/glimmer/lib/utils/outlet.d.ts +3 -2
  28. package/types/stable/index.d.ts +1 -0
@@ -7,7 +7,7 @@ import { l as lookupDescriptor } from '../../shared-chunks/mandatory-setter-BiXq
7
7
  import { isDevelopingApp } from '@embroider/macros';
8
8
  import { i as isProxy } from '../../shared-chunks/is_proxy-DjvCKvd5.js';
9
9
  import '../../@glimmer/destroyable/index.js';
10
- import '../../@glimmer/manager/index.js';
10
+ import { hasInternalComponentManager } from '../../@glimmer/manager/index.js';
11
11
  import { s as set } from '../../shared-chunks/property_set-4etrFh8A.js';
12
12
  import { g as getProperties, s as setProperties } from '../../shared-chunks/set_properties-BQFOCF2x.js';
13
13
  import { E as ENV } from '../../shared-chunks/env-mInZ1DuF.js';
@@ -1324,17 +1324,59 @@ function buildRenderState(route) {
1324
1324
  let controller = owner.lookup(`controller:${route.controllerName || name}`);
1325
1325
  (isDevelopingApp() && !(controller instanceof Controller) && assert('Expected an instance of controller', controller instanceof Controller));
1326
1326
  let model = route.currentModel;
1327
- let template = owner.lookup(`template:${route.templateName || name}`);
1327
+ let templateFactoryOrComponent = owner.lookup(`template:${route.templateName || name}`);
1328
+
1329
+ // Now we support either a component or a template to be returned by this
1330
+ // resolver call, but if it's a `TemplateFactory`, we need to instantiate
1331
+ // it into a `Template`, since that's what `RenderState` wants. We can't
1332
+ // easily change it, it's intimate API used by @ember/test-helpers and the
1333
+ // like. We could compatibly allow `Template` | `TemplateFactory`, and that's
1334
+ // what it used to do but we _just_ went through deprecations to get that
1335
+ // removed. It's also not ideal since once you mix the two types, they are
1336
+ // not exactly easy to tell apart.
1337
+ //
1338
+ // It may also be tempting to just normalize `Template` into `RouteTemplate`
1339
+ // here, and we could. However, this is not the only entrypoint where this
1340
+ // `RenderState` is made – @ember/test-helpers punches through an impressive
1341
+ // amount of private API to set it directly, and this feature would also be
1342
+ // useful for them. So, even if we had normalized here, we'd still have to
1343
+ // check and do that again during render anyway.
1344
+ let template;
1345
+ if (templateFactoryOrComponent) {
1346
+ if (hasInternalComponentManager(templateFactoryOrComponent)) {
1347
+ template = templateFactoryOrComponent;
1348
+ } else {
1349
+ if (isDevelopingApp() && typeof templateFactoryOrComponent !== 'function') {
1350
+ let label;
1351
+ try {
1352
+ label = `\`${String(templateFactoryOrComponent)}\``;
1353
+ } catch {
1354
+ label = 'an unknown object';
1355
+ }
1356
+ (isDevelopingApp() && !(false) && assert(`Failed to render the ${name} route, expected ` + `\`template:${route.templateName || name}\` to resolve into ` + `a component or a \`TemplateFactory\`, got: ${label}. ` + `Most likely an improperly defined class or an invalid module export.`));
1357
+ }
1358
+ template = templateFactoryOrComponent(owner);
1359
+ }
1360
+ } else {
1361
+ // default `{{outlet}}`
1362
+ template = route._topLevelViewTemplate(owner);
1363
+ }
1328
1364
  let render = {
1329
1365
  owner,
1330
1366
  name,
1331
1367
  controller,
1332
1368
  model,
1333
- template: template?.(owner) ?? route._topLevelViewTemplate(owner)
1369
+ template
1334
1370
  };
1335
1371
  if (isDevelopingApp()) {
1336
1372
  let LOG_VIEW_LOOKUPS = get(route._router, 'namespace.LOG_VIEW_LOOKUPS');
1337
- if (LOG_VIEW_LOOKUPS && !template) {
1373
+ // This is covered by tests and the existing code was deliberately
1374
+ // targeting the value prior to normalization, but is this message actually
1375
+ // accurate? It seems like we will always default the `{{outlet}}` template
1376
+ // so I'm not sure about "Nothing will be rendered?" (who consumes these
1377
+ // logs anyway? as lookups happen more infrequently now I doubt this is all
1378
+ // that useful)
1379
+ if (LOG_VIEW_LOOKUPS && !templateFactoryOrComponent) {
1338
1380
  info(`Could not find "${name}" template. Nothing will be rendered`, {
1339
1381
  fullName: `template:${name}`
1340
1382
  });
@@ -1,5 +1,5 @@
1
1
  import '../../@glimmer/opcode-compiler/index.js';
2
- export { a as htmlSafe, i as isHTMLSafe } from '../../shared-chunks/index-C7oKNWrX.js';
2
+ export { a as htmlSafe, i as isHTMLSafe } from '../../shared-chunks/index-4KqgXTKl.js';
3
3
  import '../../shared-chunks/registry-B8WARvkP.js';
4
4
  import '../debug/index.js';
5
5
  import '../../@glimmer/runtime/index.js';
@@ -39,7 +39,7 @@ import MutableEnumerable from '../@ember/enumerable/mutable.js';
39
39
  import '../@ember/-internals/runtime/lib/mixins/target_action_support.js';
40
40
  import '../@ember/-internals/runtime/lib/ext/rsvp.js';
41
41
  import { templateFactory } from '../@glimmer/opcode-compiler/index.js';
42
- import { I as Input, C as Component, H as Helper, e as escapeExpression } from '../shared-chunks/index-C7oKNWrX.js';
42
+ import { I as Input, C as Component, H as Helper, e as escapeExpression } from '../shared-chunks/index-4KqgXTKl.js';
43
43
  import { a as getTemplates, b as setTemplates } from '../shared-chunks/template_registry-DigcUg9m.js';
44
44
  import { isSerializationFirstNode, templateOnlyComponent, invokeHelper, hash, array, concat, get as get$1, on as on$1, fn } from '../@glimmer/runtime/index.js';
45
45
  import { run } from '../@ember/runloop/index.js';
@@ -1,4 +1,4 @@
1
1
  // this file gets replaced with the real value during the build
2
- const Version = '6.3.0-alpha.2';
2
+ const Version = '6.3.0-alpha.3';
3
3
 
4
4
  export { Version as default };
@@ -36,7 +36,7 @@ import '../../shared-chunks/unrecognized-url-error-zpz-JEoG.js';
36
36
  import '../../@ember/routing/lib/routing-service.js';
37
37
  import '../../@ember/controller/index.js';
38
38
  import '../../@glimmer/opcode-compiler/index.js';
39
- import '../../shared-chunks/index-C7oKNWrX.js';
39
+ import '../../shared-chunks/index-4KqgXTKl.js';
40
40
  import '../../shared-chunks/registry-B8WARvkP.js';
41
41
  import '../../@glimmer/runtime/index.js';
42
42
  import '../../@glimmer/reference/index.js';
@@ -3,10 +3,10 @@ import { g as getFactoryFor, p as privatize } from './registry-B8WARvkP.js';
3
3
  import { warn, debugFreeze, deprecate } from '../@ember/debug/index.js';
4
4
  import { reifyPositional, normalizeProperty, EMPTY_ARGS, createCapturedArgs, EMPTY_POSITIONAL, curry, hash, array, concat, fn, get as get$1, templateOnlyComponent, TEMPLATE_ONLY_COMPONENT_MANAGER, on as on$1, runtimeContext, DOMTreeConstruction, DOMChanges, clientBuilder, inTransaction, renderMain } from '../@glimmer/runtime/index.js';
5
5
  import { join, _backburner, schedule, _getCurrentRunLoop } from '../@ember/runloop/index.js';
6
- import { valueForRef, isConstRef, createConstRef, isUpdatableRef, updateRef, createPrimitiveRef, childRefFor, createComputeRef, childRefFromParts, isInvokableRef, createUnboundRef, createInvokableRef, createReadOnlyRef, createDebugAliasRef, UNDEFINED_REFERENCE } from '../@glimmer/reference/index.js';
6
+ import { valueForRef, isConstRef, createConstRef, isUpdatableRef, updateRef, createPrimitiveRef, childRefFor, createComputeRef, childRefFromParts, isInvokableRef, createUnboundRef, UNDEFINED_REFERENCE, createInvokableRef, createReadOnlyRef, createDebugAliasRef } from '../@glimmer/reference/index.js';
7
7
  import { untrack, consumeTag, tagFor, createCache, getValue, valueForTag, beginUntrackFrame, endUntrackFrame, beginTrackFrame, endTrackFrame, validateTag, createTag, dirtyTag as DIRTY_TAG$1, CONSTANT_TAG, isTracking, debug, createUpdatableTag, CURRENT_TAG } from '../@glimmer/validator/index.js';
8
8
  import { isDevelopingApp } from '@embroider/macros';
9
- import { setInternalComponentManager, setComponentTemplate, setInternalHelperManager, setHelperManager, getInternalHelperManager, helperCapabilities, capabilityFlagsFrom, setInternalModifierManager, getInternalComponentManager, getComponentTemplate } from '../@glimmer/manager/index.js';
9
+ import { setInternalComponentManager, setComponentTemplate, setInternalHelperManager, setHelperManager, getInternalHelperManager, helperCapabilities, capabilityFlagsFrom, setInternalModifierManager, hasInternalComponentManager, getInternalComponentManager, getComponentTemplate } from '../@glimmer/manager/index.js';
10
10
  import { h as hasDOM } from './index-BGP1rw3B.js';
11
11
  import { action as action$1 } from '../@ember/object/index.js';
12
12
  import { on } from '../@ember/modifier/on.js';
@@ -188,7 +188,7 @@ function deopaquify(opaque) {
188
188
  (isDevelopingApp() && !(constructor) && assert(`[BUG] Invalid internal component constructor: ${opaque}`, constructor));
189
189
  return constructor;
190
190
  }
191
- const CAPABILITIES$2 = {
191
+ const CAPABILITIES$3 = {
192
192
  dynamicLayout: false,
193
193
  dynamicTag: false,
194
194
  prepareArgs: false,
@@ -205,7 +205,7 @@ const CAPABILITIES$2 = {
205
205
  };
206
206
  class InternalManager {
207
207
  getCapabilities() {
208
- return CAPABILITIES$2;
208
+ return CAPABILITIES$3;
209
209
  }
210
210
  create(owner, definition, args, _env, _dynamicScope, caller) {
211
211
  (isDevelopingApp() && !(isConstRef(caller)) && assert('caller must be const', isConstRef(caller)));
@@ -3571,7 +3571,7 @@ function instrumentationPayload$1(def) {
3571
3571
  object: `${def.name}:main`
3572
3572
  };
3573
3573
  }
3574
- const CAPABILITIES$1 = {
3574
+ const CAPABILITIES$2 = {
3575
3575
  dynamicLayout: false,
3576
3576
  dynamicTag: false,
3577
3577
  prepareArgs: false,
@@ -3586,27 +3586,34 @@ const CAPABILITIES$1 = {
3586
3586
  willDestroy: false,
3587
3587
  hasSubOwner: false
3588
3588
  };
3589
+ const CAPABILITIES_MASK$1 = capabilityFlagsFrom(CAPABILITIES$2);
3589
3590
  class OutletComponentManager {
3590
3591
  create(_owner, definition, _args, env, dynamicScope) {
3591
3592
  let parentStateRef = dynamicScope.get('outletState');
3592
3593
  let currentStateRef = definition.ref;
3594
+
3595
+ // This is the actual primary responsibility of the outlet component –
3596
+ // it represents the switching from one route component/template into
3597
+ // the next. The rest only exists to support the debug render tree and
3598
+ // the old-school (and unreliable) instrumentation.
3593
3599
  dynamicScope.set('outletState', currentStateRef);
3594
3600
  let state = {
3595
- self: createConstRef(definition.controller, 'this'),
3596
3601
  finalize: _instrumentStart('render.outlet', instrumentationPayload$1, definition)
3597
3602
  };
3598
3603
  if (env.debugRenderTree !== undefined) {
3599
- state.outletBucket = {};
3600
3604
  let parentState = valueForRef(parentStateRef);
3601
- let parentOwner = parentState && parentState.render && parentState.render.owner;
3602
- let currentOwner = valueForRef(currentStateRef).render.owner;
3605
+ let parentOwner = parentState?.render?.owner;
3606
+ let currentState = valueForRef(currentStateRef);
3607
+ let currentOwner = currentState?.render?.owner;
3603
3608
  if (parentOwner && parentOwner !== currentOwner) {
3604
3609
  (isDevelopingApp() && !(currentOwner instanceof EngineInstance) && assert('Expected currentOwner to be an EngineInstance', currentOwner instanceof EngineInstance));
3605
- let mountPoint = currentOwner.mountPoint;
3606
- state.engine = currentOwner;
3610
+ let {
3611
+ mountPoint
3612
+ } = currentOwner;
3607
3613
  if (mountPoint) {
3608
- state.engineBucket = {
3609
- mountPoint
3614
+ state.engine = {
3615
+ mountPoint,
3616
+ instance: currentOwner
3610
3617
  };
3611
3618
  }
3612
3619
  }
@@ -3616,13 +3623,12 @@ class OutletComponentManager {
3616
3623
  getDebugName({
3617
3624
  name
3618
3625
  }) {
3619
- return name;
3626
+ return `{{outlet}} for ${name}`;
3620
3627
  }
3621
- getDebugCustomRenderTree(definition, state, args) {
3628
+ getDebugCustomRenderTree(_definition, state) {
3622
3629
  let nodes = [];
3623
- (isDevelopingApp() && !(state.outletBucket) && assert('[BUG] outletBucket must be set', state.outletBucket));
3624
3630
  nodes.push({
3625
- bucket: state.outletBucket,
3631
+ bucket: state,
3626
3632
  type: 'outlet',
3627
3633
  // "main" used to be the outlet name, keeping it around for compatibility
3628
3634
  name: 'main',
@@ -3630,33 +3636,23 @@ class OutletComponentManager {
3630
3636
  instance: undefined,
3631
3637
  template: undefined
3632
3638
  });
3633
- if (state.engineBucket) {
3639
+ if (state.engine) {
3634
3640
  nodes.push({
3635
- bucket: state.engineBucket,
3641
+ bucket: state.engine,
3636
3642
  type: 'engine',
3637
- name: state.engineBucket.mountPoint,
3643
+ name: state.engine.mountPoint,
3638
3644
  args: EMPTY_ARGS,
3639
- instance: state.engine,
3645
+ instance: state.engine.instance,
3640
3646
  template: undefined
3641
3647
  });
3642
3648
  }
3643
- nodes.push({
3644
- bucket: state,
3645
- type: 'route-template',
3646
- name: definition.name,
3647
- args: args,
3648
- instance: definition.controller,
3649
- template: unwrapTemplate(definition.template).moduleName
3650
- });
3651
3649
  return nodes;
3652
3650
  }
3653
3651
  getCapabilities() {
3654
- return CAPABILITIES$1;
3652
+ return CAPABILITIES$2;
3655
3653
  }
3656
- getSelf({
3657
- self
3658
- }) {
3659
- return self;
3654
+ getSelf() {
3655
+ return UNDEFINED_REFERENCE;
3660
3656
  }
3661
3657
  didCreate() {}
3662
3658
  didUpdate() {}
@@ -3669,23 +3665,30 @@ class OutletComponentManager {
3669
3665
  }
3670
3666
  }
3671
3667
  const OUTLET_MANAGER = new OutletComponentManager();
3672
- class OutletComponentDefinition {
3668
+ const OUTLET_COMPONENT_TEMPLATE = templateFactory(
3669
+ /*
3670
+ <@Component @controller={{@controller}} @model={{@model}} />
3671
+ */
3672
+ {
3673
+ "id": "tiv/fOHO",
3674
+ "block": "[[[8,[30,1],null,[[\"@controller\",\"@model\"],[[30,2],[30,3]]],null]],[\"@Component\",\"@controller\",\"@model\"],false,[]]",
3675
+ "moduleName": "/home/runner/work/ember.js/ember.js/packages/@ember/-internals/glimmer/lib/component-managers/outlet.ts",
3676
+ "isStrictMode": true
3677
+ });
3678
+ class OutletComponent {
3673
3679
  // handle is not used by this custom definition
3674
3680
  handle = -1;
3675
- resolvedName;
3681
+ resolvedName = null;
3682
+ manager = OUTLET_MANAGER;
3683
+ capabilities = CAPABILITIES_MASK$1;
3676
3684
  compilable;
3677
- capabilities;
3678
- constructor(state, manager = OUTLET_MANAGER) {
3685
+ constructor(owner, state) {
3679
3686
  this.state = state;
3680
- this.manager = manager;
3681
- let capabilities = manager.getCapabilities();
3682
- this.capabilities = capabilityFlagsFrom(capabilities);
3683
- this.compilable = capabilities.wrapped ? unwrapTemplate(state.template).asWrappedLayout() : unwrapTemplate(state.template).asLayout();
3684
- this.resolvedName = state.name;
3687
+ this.compilable = unwrapTemplate(OUTLET_COMPONENT_TEMPLATE(owner)).asLayout();
3685
3688
  }
3686
3689
  }
3687
3690
  function createRootOutlet(outletView) {
3688
- return new OutletComponentDefinition(outletView.state);
3691
+ return new OutletComponent(outletView.owner, outletView.state);
3689
3692
  }
3690
3693
 
3691
3694
  class RootComponentManager extends CurlyComponentManager {
@@ -4511,7 +4514,7 @@ class ActionModifierManager {
4511
4514
  const ACTION_MODIFIER_MANAGER = new ActionModifierManager();
4512
4515
  const actionModifier = setInternalModifierManager(ACTION_MODIFIER_MANAGER, {});
4513
4516
 
4514
- const CAPABILITIES = {
4517
+ const CAPABILITIES$1 = {
4515
4518
  dynamicLayout: true,
4516
4519
  dynamicTag: false,
4517
4520
  prepareArgs: false,
@@ -4532,7 +4535,7 @@ class MountManager {
4532
4535
  return unwrapTemplate(templateFactory(state.engine)).asLayout();
4533
4536
  }
4534
4537
  getCapabilities() {
4535
- return CAPABILITIES;
4538
+ return CAPABILITIES$1;
4536
4539
  }
4537
4540
  getOwner(state) {
4538
4541
  return state.engine;
@@ -4633,7 +4636,7 @@ class MountDefinition {
4633
4636
  state;
4634
4637
  manager = MOUNT_MANAGER;
4635
4638
  compilable = null;
4636
- capabilities = capabilityFlagsFrom(CAPABILITIES);
4639
+ capabilities = capabilityFlagsFrom(CAPABILITIES$1);
4637
4640
  constructor(resolvedName) {
4638
4641
  this.resolvedName = resolvedName;
4639
4642
  this.state = {
@@ -4677,6 +4680,110 @@ const mountHelper = internalHelper((args, owner) => {
4677
4680
  });
4678
4681
  });
4679
4682
 
4683
+ const CAPABILITIES = {
4684
+ dynamicLayout: false,
4685
+ dynamicTag: false,
4686
+ prepareArgs: false,
4687
+ createArgs: true,
4688
+ attributeHook: false,
4689
+ elementHook: false,
4690
+ createCaller: false,
4691
+ dynamicScope: false,
4692
+ updateHook: false,
4693
+ createInstance: true,
4694
+ wrapped: false,
4695
+ willDestroy: false,
4696
+ hasSubOwner: false
4697
+ };
4698
+ const CAPABILITIES_MASK = capabilityFlagsFrom(CAPABILITIES);
4699
+ class RouteTemplateManager {
4700
+ create(_owner, _definition, args) {
4701
+ let self = args.named.get('controller');
4702
+ if (isDevelopingApp()) {
4703
+ self = createDebugAliasRef('this', self);
4704
+ }
4705
+ let controller = valueForRef(self);
4706
+ return {
4707
+ self,
4708
+ controller
4709
+ };
4710
+ }
4711
+ getSelf({
4712
+ self
4713
+ }) {
4714
+ return self;
4715
+ }
4716
+ getDebugName({
4717
+ name
4718
+ }) {
4719
+ return `route-template (${name})`;
4720
+ }
4721
+ getDebugCustomRenderTree({
4722
+ name,
4723
+ templateName
4724
+ }, state, args) {
4725
+ return [{
4726
+ bucket: state,
4727
+ type: 'route-template',
4728
+ name,
4729
+ args,
4730
+ instance: state.controller,
4731
+ template: templateName
4732
+ }];
4733
+ }
4734
+ getCapabilities() {
4735
+ return CAPABILITIES;
4736
+ }
4737
+ didRenderLayout() {}
4738
+ didUpdateLayout() {}
4739
+ didCreate() {}
4740
+ didUpdate() {}
4741
+ getDestroyable() {
4742
+ return null;
4743
+ }
4744
+ }
4745
+ const ROUTE_TEMPLATE_MANAGER = new RouteTemplateManager();
4746
+
4747
+ /**
4748
+ * This "upgrades" a route template into a invocable component. Conceptually
4749
+ * it can be 1:1 for each unique `Template`, but it's also cheap to construct,
4750
+ * so unless the stability is desirable for other reasons, it's probably not
4751
+ * worth caching this.
4752
+ */
4753
+ class RouteTemplate {
4754
+ // handle is not used by this custom definition
4755
+ handle = -1;
4756
+ resolvedName;
4757
+ state;
4758
+ manager = ROUTE_TEMPLATE_MANAGER;
4759
+ capabilities = CAPABILITIES_MASK;
4760
+ compilable;
4761
+ constructor(name, template) {
4762
+ let unwrapped = unwrapTemplate(template);
4763
+ // TODO This actually seems inaccurate – it ultimately came from the
4764
+ // outlet's name. Also, setting this overrides `getDebugName()` in that
4765
+ // message. Is that desirable?
4766
+ this.resolvedName = name;
4767
+ this.state = {
4768
+ name,
4769
+ templateName: unwrapped.moduleName
4770
+ };
4771
+ this.compilable = unwrapped.asLayout();
4772
+ }
4773
+ }
4774
+
4775
+ // TODO a lot these fields are copied from the adjacent existing components
4776
+ // implementation, haven't looked into who cares about `ComponentDefinition`
4777
+ // and if it is appropriate here. It seems like this version is intended to
4778
+ // be used with `curry` which probably isn't necessary here. It could be the
4779
+ // case that we just want to do something more similar to `InternalComponent`
4780
+ // (the one we used to implement `Input` and `LinkTo`). For now it follows
4781
+ // the same pattern to get things going.
4782
+ function makeRouteTemplate(owner, name, template) {
4783
+ let routeTemplate = new RouteTemplate(name, template);
4784
+ return curry(CurriedTypes.Component, routeTemplate, owner, null, true);
4785
+ }
4786
+
4680
4787
  /**
4681
4788
  The `{{outlet}}` helper lets you specify where a child route will render in
4682
4789
  your template. An important use of the `{{outlet}}` helper is in your
@@ -4709,15 +4816,77 @@ const outletHelper = internalHelper((_args, owner, scope) => {
4709
4816
  return state?.outlets?.main;
4710
4817
  });
4711
4818
  let lastState = null;
4712
- let definition = null;
4819
+ let outlet = null;
4713
4820
  return createComputeRef(() => {
4714
4821
  let outletState = valueForRef(outletRef);
4715
4822
  let state = stateFor(outletRef, outletState);
4716
- if (!validate(state, lastState)) {
4823
+
4824
+ // This code is deliberately using the behavior in glimmer-vm where in
4825
+ // <@Component />, the component is considered stabled via `===`, and
4826
+ // will continue to re-render in-place as long as the `===` holds, but
4827
+ // when it changes to a different object, it teardown the old component
4828
+ // (running destructors, etc), and render the component in its place (or
4829
+ // nothing if the new value is nullish. Here we are carefully exploiting
4830
+ // that fact, and returns the same stable object so long as it is the
4831
+ // same route, but return a different one when the route changes. On the
4832
+ // other hand, changing the model only intentionally do not teardown the
4833
+ // component and instead re-render in-place.
4834
+ if (!isStable(state, lastState)) {
4717
4835
  lastState = state;
4718
4836
  if (state !== null) {
4837
+ // If we are crossing an engine mount point, this is how the owner
4838
+ // gets switched.
4839
+ let outletOwner = outletState?.render?.owner ?? owner;
4719
4840
  let named = dict();
4720
4841
 
4842
+ // Here we either have a raw template that needs to be normalized,
4843
+ // or a component that we can render as-is. `RouteTemplate` upgrades
4844
+ // the template into a component so we can have a unified code path.
4845
+ // We still store the original `template` value, because we rely on
4846
+ // its identity for the stability check, and the `RouteTemplate`
4847
+ // wrapper doesn't dedup for us.
4848
+ let template = state.template;
4849
+ let component;
4850
+ if (hasInternalComponentManager(template)) {
4851
+ component = template;
4852
+ } else {
4853
+ if (isDevelopingApp()) {
4854
+ // We don't appear to have a standard way or a brand to check, but for the
4855
+ // purpose of avoiding obvious user errors, this probably gets you close
4856
+ // enough.
4857
+ let isTemplate = template => {
4858
+ if (template === null || typeof template !== 'object') {
4859
+ return false;
4860
+ } else {
4861
+ let t = template;
4862
+ return t.result === 'ok' || t.result === 'error';
4863
+ }
4864
+ };
4865
+
4866
+ // We made it past the `TemplateFactory` instantiation before
4867
+ // getting here, so either we got unlucky where the invalid type
4868
+ // happens to be a function that didn't mind taking owner as an
4869
+ // argument, or this was directly set by something like test
4870
+ // helpers.
4871
+ if (!isTemplate(template)) {
4872
+ let label;
4873
+ try {
4874
+ label = `\`${String(template)}\``;
4875
+ } catch {
4876
+ label = 'an unknown object';
4877
+ }
4878
+ (isDevelopingApp() && !(false) && assert(`Failed to render the \`${state.name}\` route: expected ` + `a component or Template object, but got ${label}.`));
4879
+ }
4880
+ }
4881
+ component = makeRouteTemplate(outletOwner, state.name, template);
4882
+ }
4883
+
4884
+ // Component is stable for the lifetime of the outlet
4885
+ named['Component'] = createConstRef(component, '@Component');
4886
+
4887
+ // Controller is stable for the lifetime of the outlet
4888
+ named['controller'] = createConstRef(state.controller, '@controller');
4889
+
4721
4890
  // Create a ref for the model
4722
4891
  let modelRef = childRefFromParts(outletRef, ['render', 'model']);
4723
4892
 
@@ -4740,12 +4909,14 @@ const outletHelper = internalHelper((_args, owner, scope) => {
4740
4909
  named['model'] = createDebugAliasRef('@model', named['model']);
4741
4910
  }
4742
4911
  let args = createCapturedArgs(named, EMPTY_POSITIONAL);
4743
- definition = curry(CurriedTypes.Component, new OutletComponentDefinition(state), outletState?.render?.owner ?? owner, args, true);
4912
+
4913
+ // Package up everything
4914
+ outlet = curry(CurriedTypes.Component, new OutletComponent(owner, state), outletOwner, args, true);
4744
4915
  } else {
4745
- definition = null;
4916
+ outlet = null;
4746
4917
  }
4747
4918
  }
4748
- return definition;
4919
+ return outlet;
4749
4920
  });
4750
4921
  });
4751
4922
  function stateFor(ref, outlet) {
@@ -4753,20 +4924,19 @@ function stateFor(ref, outlet) {
4753
4924
  let render = outlet.render;
4754
4925
  if (render === undefined) return null;
4755
4926
  let template = render.template;
4756
- if (template === undefined) return null;
4927
+ // The type doesn't actually allow for `null`, but if we make it past this
4928
+ // point it is really important that we have _something_ to render. We could
4929
+ // assert, but that is probably overly strict for very little to gain.
4930
+ if (template === undefined || template === null) return null;
4757
4931
  return {
4758
4932
  ref,
4759
4933
  name: render.name,
4760
4934
  template,
4761
- controller: render.controller,
4762
- model: render.model
4935
+ controller: render.controller
4763
4936
  };
4764
4937
  }
4765
- function validate(state, lastState) {
4766
- if (state === null) {
4767
- return lastState === null;
4768
- }
4769
- if (lastState === null) {
4938
+ function isStable(state, lastState) {
4939
+ if (state === null || lastState === null) {
4770
4940
  return false;
4771
4941
  }
4772
4942
  return state.template === lastState.template && state.controller === lastState.controller;
@@ -5020,8 +5190,7 @@ class OutletView {
5020
5190
  ref,
5021
5191
  name: TOP_LEVEL_NAME,
5022
5192
  template,
5023
- controller: undefined,
5024
- model: undefined
5193
+ controller: undefined
5025
5194
  };
5026
5195
  }
5027
5196
  appendTo(selector) {
@@ -5272,8 +5441,31 @@ class Renderer {
5272
5441
  // renderer HOOKS
5273
5442
 
5274
5443
  appendOutletView(view, target) {
5275
- let definition = createRootOutlet(view);
5276
- this._appendDefinition(view, curry(CurriedTypes.Component, definition, view.owner, null, true), target);
5444
+ // TODO: This bypasses the {{outlet}} syntax so logically duplicates
5445
+ // some of the set up code. Since this is all internal (or is it?),
5446
+ // we can refactor this to do something more direct/less convoluted
5447
+ // and with less setup, but get it working first
5448
+ let outlet = createRootOutlet(view);
5449
+ let {
5450
+ name,
5451
+ /* controller, */template
5452
+ } = view.state;
5453
+ let named = dict();
5454
+ named['Component'] = createConstRef(makeRouteTemplate(view.owner, name, template), '@Component');
5455
+
5456
+ // TODO: is this guaranteed to be undefined? It seems to be the
5457
+ // case in the `OutletView` class. Investigate how much that class
5458
+ // exists as an internal implementation detail only, or if it was
5459
+ // used outside of core. As far as I can tell, test-helpers uses
5460
+ // it but only for `setOutletState`.
5461
+ // named['controller'] = createConstRef(controller, '@controller');
5462
+ // Update: at least according to the debug render tree tests, we
5463
+ // appear to always expect this to be undefined. Not a definitive
5464
+ // source by any means, but is useful evidence
5465
+ named['controller'] = UNDEFINED_REFERENCE;
5466
+ named['model'] = UNDEFINED_REFERENCE;
5467
+ let args = createCapturedArgs(named, EMPTY_POSITIONAL);
5468
+ this._appendDefinition(view, curry(CurriedTypes.Component, outlet, view.owner, args, true), target);
5277
5469
  }
5278
5470
  appendTo(view, target) {
5279
5471
  let definition = new RootComponentDefinition(view);
@@ -1,7 +1,7 @@
1
1
  import { p as privatize } from './registry-B8WARvkP.js';
2
2
  import { getOwner } from '../@ember/-internals/owner/index.js';
3
3
  import '../@ember/debug/index.js';
4
- import { R as RootTemplate, b as Renderer, O as OutletView, c as OutletTemplate, I as Input, L as LinkTo, T as Textarea } from './index-C7oKNWrX.js';
4
+ import { R as RootTemplate, b as Renderer, O as OutletView, c as OutletTemplate, I as Input, L as LinkTo, T as Textarea } from './index-4KqgXTKl.js';
5
5
  import { clientBuilder, rehydrationBuilder } from '../@glimmer/runtime/index.js';
6
6
  import { serializeBuilder } from '../@glimmer/node/index.js';
7
7
  import { isDevelopingApp } from '@embroider/macros';