Skip to content

Commit

Permalink
[FEATURE strict-mode] Enable classic component usage in strict mode
Browse files Browse the repository at this point in the history
Previously classic components were not possible to use in strict mode,
since there was no way to pass the owner to them as they were not
resolved with the factory system. This refactor fixes this by using the
new owner API, which passes the owner into the `create` hook of
component managers. In addition, makes the `renderer` property of views
a standard injection that is lazy, so we are no longer injecting it via
the implicit injection API.
  • Loading branch information
Chris Garrett committed Dec 14, 2020
1 parent 4dd8f0d commit d81b0f8
Show file tree
Hide file tree
Showing 21 changed files with 154 additions and 186 deletions.
24 changes: 12 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,19 @@
},
"devDependencies": {
"@babel/preset-env": "^7.9.5",
"@glimmer/compiler": "0.69.3",
"@glimmer/compiler": "0.70.0",
"@glimmer/env": "^0.1.7",
"@glimmer/global-context": "0.69.3",
"@glimmer/interfaces": "0.69.3",
"@glimmer/manager": "0.69.3",
"@glimmer/destroyable": "0.69.3",
"@glimmer/owner": "0.69.3",
"@glimmer/node": "0.69.3",
"@glimmer/opcode-compiler": "0.69.3",
"@glimmer/program": "0.69.3",
"@glimmer/reference": "0.69.3",
"@glimmer/runtime": "0.69.3",
"@glimmer/validator": "0.69.3",
"@glimmer/global-context": "0.70.0",
"@glimmer/interfaces": "0.70.0",
"@glimmer/manager": "0.70.0",
"@glimmer/destroyable": "0.70.0",
"@glimmer/owner": "0.70.0",
"@glimmer/node": "0.70.0",
"@glimmer/opcode-compiler": "0.70.0",
"@glimmer/program": "0.70.0",
"@glimmer/reference": "0.70.0",
"@glimmer/runtime": "0.70.0",
"@glimmer/validator": "0.70.0",
"@simple-dom/document": "^1.4.0",
"@types/qunit": "^2.9.1",
"@types/rsvp": "^4.0.3",
Expand Down
8 changes: 1 addition & 7 deletions packages/@ember/-internals/glimmer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,13 +371,7 @@ export { InputComponent as Input } from './lib/components/input';
export { default as Component } from './lib/component';
export { default as Helper, helper } from './lib/helper';
export { SafeString, escapeExpression, htmlSafe, isHTMLSafe } from './lib/utils/string';
export {
Renderer,
InertRenderer,
InteractiveRenderer,
_resetRenderers,
renderSettled,
} from './lib/renderer';
export { Renderer, _resetRenderers, renderSettled } from './lib/renderer';
export {
getTemplate,
setTemplate,
Expand Down
36 changes: 20 additions & 16 deletions packages/@ember/-internals/glimmer/lib/component-managers/curly.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Factory, getOwner } from '@ember/-internals/owner';
import { Factory, getOwner, Owner, setOwner } from '@ember/-internals/owner';
import { enumerableSymbol, guidFor, symbol } from '@ember/-internals/utils';
import { addChildView, setElementView, setViewElement } from '@ember/-internals/views';
import { assert, debugFreeze } from '@ember/debug';
Expand Down Expand Up @@ -131,7 +131,7 @@ type ComponentFactory = Factory<

export default class CurlyComponentManager
implements
WithCreateInstance<ComponentStateBucket, Environment>,
WithCreateInstance<ComponentStateBucket>,
WithDynamicLayout<ComponentStateBucket, RuntimeResolver>,
WithDynamicTagName<ComponentStateBucket> {
protected templateFor(component: Component): CompilableProgram | null {
Expand Down Expand Up @@ -250,9 +250,10 @@ export default class CurlyComponentManager
* etc.
*/
create(
environment: Environment,
owner: Owner,
ComponentClass: ComponentFactory,
args: VMArguments,
{ isInteractive }: Environment,
dynamicScope: DynamicScope,
callerSelfRef: Reference,
hasBlock: boolean
Expand Down Expand Up @@ -285,6 +286,8 @@ export default class CurlyComponentManager
// `_target`, so bubbled actions are routed to the right place.
props._target = valueForRef(callerSelfRef);

setOwner(props, owner);

// caller:
// <FaIcon @name="bug" />
//
Expand Down Expand Up @@ -314,26 +317,26 @@ export default class CurlyComponentManager

// We usually do this in the `didCreateElement`, but that hook doesn't fire for tagless components
if (!hasWrappedElement) {
if (environment.isInteractive) {
if (isInteractive) {
component.trigger('willRender');
}

component._transitionTo('hasElement');

if (environment.isInteractive) {
if (isInteractive) {
component.trigger('willInsertElement');
}
}

// Track additional lifecycle metadata about this component in a state bucket.
// Essentially we're saving off all the state we'll need in the future.
let bucket = new ComponentStateBucket(
environment,
component,
capturedArgs,
argsTag,
finalizer,
hasWrappedElement
hasWrappedElement,
isInteractive
);

if (args.named.has('class')) {
Expand All @@ -344,7 +347,7 @@ export default class CurlyComponentManager
processComponentInitializationAssertions(component, props);
}

if (environment.isInteractive && hasWrappedElement) {
if (isInteractive && hasWrappedElement) {
component.trigger('willRender');
}

Expand All @@ -368,7 +371,7 @@ export default class CurlyComponentManager
}

didCreateElement(
{ component, classRef, environment, rootRef }: ComponentStateBucket,
{ component, classRef, isInteractive, rootRef }: ComponentStateBucket,
element: SimpleElement,
operations: ElementOperations
): void {
Expand Down Expand Up @@ -411,7 +414,7 @@ export default class CurlyComponentManager

component._transitionTo('hasElement');

if (environment.isInteractive) {
if (isInteractive) {
beginUntrackFrame();
component.trigger('willInsertElement');
endUntrackFrame();
Expand All @@ -423,16 +426,16 @@ export default class CurlyComponentManager
bucket.finalize();
}

didCreate({ component, environment }: ComponentStateBucket): void {
if (environment.isInteractive) {
didCreate({ component, isInteractive }: ComponentStateBucket): void {
if (isInteractive) {
component._transitionTo('inDOM');
component.trigger('didInsertElement');
component.trigger('didRender');
}
}

update(bucket: ComponentStateBucket): void {
let { component, args, argsTag, argsRevision, environment } = bucket;
let { component, args, argsTag, argsRevision, isInteractive } = bucket;

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

Expand All @@ -453,7 +456,7 @@ export default class CurlyComponentManager
component.trigger('didReceiveAttrs');
}

if (environment.isInteractive) {
if (isInteractive) {
component.trigger('willUpdate');
component.trigger('willRender');
}
Expand All @@ -468,8 +471,8 @@ export default class CurlyComponentManager
bucket.finalize();
}

didUpdate({ component, environment }: ComponentStateBucket): void {
if (environment.isInteractive) {
didUpdate({ component, isInteractive }: ComponentStateBucket): void {
if (isInteractive) {
component.trigger('didUpdate');
component.trigger('didRender');
}
Expand Down Expand Up @@ -553,6 +556,7 @@ export const CURLY_CAPABILITIES: InternalComponentCapabilities = {
createInstance: true,
wrapped: true,
willDestroy: true,
hasSubOwner: false,
};

export const CURLY_COMPONENT_MANAGER = new CurlyComponentManager();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const CAPABILITIES: InternalComponentCapabilities = {
createInstance: true,
wrapped: false,
willDestroy: false,
hasSubOwner: false,
};

export interface InternalComponentState {
Expand All @@ -40,27 +41,21 @@ export default class InternalManager
implements
InternalComponentManager<InternalComponentState, EmberInternalComponentConstructor>,
WithCreateInstance {
static for(
definition: EmberInternalComponentConstructor,
name: string
): (owner: Owner) => InternalManager {
return (owner: Owner) => new InternalManager(owner, definition, name);
static for(definition: EmberInternalComponentConstructor, name: string): () => InternalManager {
return () => new InternalManager(definition, name);
}

constructor(
private owner: Owner,
private ComponentClass: EmberInternalComponentConstructor,
private name: string
) {}
constructor(private ComponentClass: EmberInternalComponentConstructor, private name: string) {}

getCapabilities(): InternalComponentCapabilities {
return CAPABILITIES;
}

create(
env: Environment,
owner: Owner,
_definition: unknown,
args: VMArguments,
env: Environment,
_dynamicScope: DynamicScope,
caller: Reference
): InternalComponentState {
Expand All @@ -71,7 +66,7 @@ export default class InternalManager
args.positional.length === 0
);

let { ComponentClass, owner } = this;
let { ComponentClass } = this;
let instance = new ComponentClass(owner, args.named.capture(), valueForRef(caller));

let state = { env, instance };
Expand Down
15 changes: 11 additions & 4 deletions packages/@ember/-internals/glimmer/lib/component-managers/mount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
WithCreateInstance,
WithCustomDebugRenderTree,
WithDynamicLayout,
WithSubOwner,
} from '@glimmer/interfaces';
import { capabilityFlagsFrom } from '@glimmer/manager';
import { createConstRef, Reference, valueForRef } from '@glimmer/reference';
Expand Down Expand Up @@ -45,13 +46,15 @@ const CAPABILITIES = {
createInstance: true,
wrapped: false,
willDestroy: false,
hasSubOwner: true,
};

class MountManager
implements
WithCreateInstance<EngineState, Environment>,
WithCreateInstance<EngineState>,
WithDynamicLayout<EngineState, RuntimeResolver>,
WithCustomDebugRenderTree<EngineState, EngineDefinitionState> {
WithCustomDebugRenderTree<EngineState, EngineDefinitionState>,
WithSubOwner<EngineState> {
getDynamicLayout(state: EngineState) {
let templateFactory = state.engine.lookup('template:application') as TemplateFactory;
return unwrapTemplate(templateFactory(state.engine)).asLayout();
Expand All @@ -61,13 +64,17 @@ class MountManager
return CAPABILITIES;
}

create(env: Environment<Owner>, { name }: EngineDefinitionState, args: VMArguments) {
getOwner(state: EngineState) {
return state.engine;
}

create(owner: Owner, { name }: EngineDefinitionState, args: VMArguments, env: Environment) {
// TODO
// mount is a runtime helper, this shouldn't use dynamic layout
// we should resolve the engine app template in the helper
// it also should use the owner that looked up the mount helper.

let engine = env.owner.buildChildEngineInstance(name);
let engine = owner.buildChildEngineInstance(name);

engine.boot();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ENV } from '@ember/-internals/environment';
import { Owner } from '@ember/-internals/owner';
import { guidFor } from '@ember/-internals/utils';
import { assert } from '@ember/debug';
import EngineInstance from '@ember/engine/instance';
Expand Down Expand Up @@ -64,16 +65,18 @@ const CAPABILITIES: InternalComponentCapabilities = {
createInstance: true,
wrapped: false,
willDestroy: false,
hasSubOwner: false,
};

class OutletComponentManager
implements
WithCreateInstance<OutletInstanceState, Environment>,
WithCreateInstance<OutletInstanceState>,
WithCustomDebugRenderTree<OutletInstanceState, OutletDefinitionState> {
create(
env: Environment,
_owner: Owner,
definition: OutletDefinitionState,
_args: VMArguments,
env: Environment,
dynamicScope: DynamicScope
): OutletInstanceState {
let parentStateRef = dynamicScope.get('outletState');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Environment,
InternalComponentCapabilities,
Option,
Owner,
VMArguments,
} from '@glimmer/interfaces';
import { capabilityFlagsFrom } from '@glimmer/manager';
Expand All @@ -27,9 +28,10 @@ class RootComponentManager extends CurlyComponentManager {
}

create(
environment: Environment,
_owner: Owner,
_state: unknown,
_args: Option<VMArguments>,
{ isInteractive }: Environment,
dynamicScope: DynamicScope
) {
let component = this.component;
Expand All @@ -42,13 +44,13 @@ class RootComponentManager extends CurlyComponentManager {

// We usually do this in the `didCreateElement`, but that hook doesn't fire for tagless components
if (!hasWrappedElement) {
if (environment.isInteractive) {
if (isInteractive) {
component.trigger('willRender');
}

component._transitionTo('hasElement');

if (environment.isInteractive) {
if (isInteractive) {
component.trigger('willInsertElement');
}
}
Expand All @@ -58,12 +60,12 @@ class RootComponentManager extends CurlyComponentManager {
}

let bucket = new ComponentStateBucket(
environment,
component,
null,
CONSTANT_TAG,
finalizer,
hasWrappedElement
hasWrappedElement,
isInteractive
);

consumeTag(component[DIRTY_TAG]);
Expand All @@ -87,6 +89,7 @@ export const ROOT_CAPABILITIES: InternalComponentCapabilities = {
createInstance: true,
wrapped: true,
willDestroy: false,
hasSubOwner: false,
};

export class RootComponentDefinition implements ComponentDefinition {
Expand Down
2 changes: 1 addition & 1 deletion packages/@ember/-internals/glimmer/lib/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,7 @@ const Component = CoreView.extend(
this[DIRTY_TAG] = createTag();
this[BOUNDS] = null;

if (DEBUG && this.renderer._destinedForDOM && this.tagName === '') {
if (DEBUG && this.renderer._isInteractive && this.tagName === '') {
let eventNames = [];
let eventDispatcher = getOwner(this).lookup<any | undefined>('event_dispatcher:main');
let events = (eventDispatcher && eventDispatcher._finalEvents) || {};
Expand Down
Loading

0 comments on commit d81b0f8

Please sign in to comment.