Skip to content

Commit

Permalink
[FEATURE dynamic-helper-and-modifier] Enable dynamic helper usage
Browse files Browse the repository at this point in the history
Enables dynamic helper usage based on either helper definitions
directly, or currying helpers with the (helper) keyword.
  • Loading branch information
Chris Garrett committed Dec 15, 2020
1 parent 9588535 commit 7e14be6
Show file tree
Hide file tree
Showing 24 changed files with 245 additions and 1,089 deletions.
Expand Up @@ -41,10 +41,6 @@ export default class InternalManager
implements
InternalComponentManager<InternalComponentState, EmberInternalComponentConstructor>,
WithCreateInstance {
static for(definition: EmberInternalComponentConstructor, name: string): () => InternalManager {
return () => new InternalManager(definition, name);
}

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

getCapabilities(): InternalComponentCapabilities {
Expand Down
2 changes: 1 addition & 1 deletion packages/@ember/-internals/glimmer/lib/component.ts
Expand Up @@ -1128,6 +1128,6 @@ Component.reopenClass({
positionalParams: [],
});

setInternalComponentManager(() => CURLY_COMPONENT_MANAGER, Component);
setInternalComponentManager(CURLY_COMPONENT_MANAGER, Component);

export default Component;
2 changes: 1 addition & 1 deletion packages/@ember/-internals/glimmer/lib/components/input.ts
Expand Up @@ -126,7 +126,7 @@ export const InputComponent = {
},
};

setInternalComponentManager(InternalManager.for(Input, 'input'), InputComponent);
setInternalComponentManager(new InternalManager(Input, 'input'), InputComponent);
setComponentTemplate(InputTemplate, InputComponent);

Input.toString = () => '@ember/component/input';
8 changes: 4 additions & 4 deletions packages/@ember/-internals/glimmer/lib/helper.ts
Expand Up @@ -8,7 +8,7 @@ import { getDebugName, symbol } from '@ember/-internals/utils';
import { join } from '@ember/runloop';
import { DEBUG } from '@glimmer/env';
import { Arguments, Dict, HelperManager } from '@glimmer/interfaces';
import { helperCapabilities, setHelperManager } from '@glimmer/manager';
import { getInternalHelperManager, helperCapabilities, setHelperManager } from '@glimmer/manager';
import {
consumeTag,
createTag,
Expand Down Expand Up @@ -195,11 +195,11 @@ class ClassicHelperManager implements HelperManager<ClassicHelperStateBucket> {
}
}

export const CLASSIC_HELPER_MANAGER_FACTORY = (owner: Owner | undefined): ClassicHelperManager => {
setHelperManager((owner: Owner | undefined): ClassicHelperManager => {
return new ClassicHelperManager(owner);
};
}, Helper);

setHelperManager(CLASSIC_HELPER_MANAGER_FACTORY, Helper);
export const CLASSIC_HELPER_MANAGER = getInternalHelperManager(Helper);

///////////

Expand Down

This file was deleted.

Expand Up @@ -2,5 +2,5 @@ import { Helper, HelperDefinitionState } from '@glimmer/interfaces';
import { setInternalHelperManager } from '@glimmer/manager';

export function internalHelper(helper: Helper): HelperDefinitionState {
return setInternalHelperManager(() => helper, {});
return setInternalHelperManager(helper, {});
}
4 changes: 3 additions & 1 deletion packages/@ember/-internals/glimmer/lib/modifiers/action.ts
@@ -1,3 +1,4 @@
import { Owner } from '@ember/-internals/owner';
import { uuid } from '@ember/-internals/utils';
import { ActionManager, isSimpleClick } from '@ember/-internals/views';
import { assert, deprecate } from '@ember/debug';
Expand Down Expand Up @@ -204,6 +205,7 @@ export class ActionState {

class ActionModifierManager implements InternalModifierManager<ActionState, object> {
create(
_owner: Owner,
element: SimpleElement,
_state: object,
args: VMArguments,
Expand Down Expand Up @@ -312,4 +314,4 @@ class ActionModifierManager implements InternalModifierManager<ActionState, obje

const ACTION_MODIFIER_MANAGER = new ActionModifierManager();

export default setInternalModifierManager(() => ACTION_MODIFIER_MANAGER, {});
export default setInternalModifierManager(ACTION_MODIFIER_MANAGER, {});
12 changes: 4 additions & 8 deletions packages/@ember/-internals/glimmer/lib/modifiers/internal.ts
Expand Up @@ -63,23 +63,19 @@ class InternalModifierState implements Destroyable {

class InternalModifierManager
implements ModifierManager<InternalModifierState, typeof InternalModifier> {
constructor(private readonly owner: Owner) {}

create(
owner: Owner,
element: SimpleElement,
factory: typeof InternalModifier,
args: VMArguments
): InternalModifierState {
assert('element must be an HTMLElement', element instanceof HTMLElement);

let instance = new factory(this.owner, element, args.capture());
let instance = new factory(owner, element, args.capture());

registerDestructor(instance, (modifier) => modifier.remove());

return new InternalModifierState(
factory.name,
new factory(this.owner, element, args.capture())
);
return new InternalModifierState(factory.name, instance);
}

// not needed for now, but feel free to implement this
Expand All @@ -105,4 +101,4 @@ class InternalModifierManager
}
}

setInternalModifierManager((owner: Owner) => new InternalModifierManager(owner), InternalModifier);
setInternalModifierManager(new InternalModifierManager(), InternalModifier);
4 changes: 3 additions & 1 deletion packages/@ember/-internals/glimmer/lib/modifiers/on.ts
@@ -1,3 +1,4 @@
import { Owner } from '@ember/-internals/owner';
import { assert } from '@ember/debug';
import { registerDestructor } from '@glimmer/destroyable';
import { DEBUG } from '@glimmer/env';
Expand Down Expand Up @@ -322,6 +323,7 @@ class OnModifierManager implements InternalModifierManager<OnModifierState | nul
}

create(
_owner: Owner,
element: SimpleElement | Element,
_state: object,
args: VMArguments
Expand Down Expand Up @@ -387,6 +389,6 @@ const ON_MODIFIER_MANAGER = new OnModifierManager();

const on = {};

setInternalModifierManager(() => ON_MODIFIER_MANAGER, on);
setInternalModifierManager(ON_MODIFIER_MANAGER, on);

export default on;
12 changes: 5 additions & 7 deletions packages/@ember/-internals/glimmer/lib/resolver.ts
Expand Up @@ -31,13 +31,12 @@ import {
import { _WeakSet } from '@glimmer/util';
import { isCurlyManager } from './component-managers/curly';
import {
CLASSIC_HELPER_MANAGER_FACTORY,
CLASSIC_HELPER_MANAGER,
HelperFactory,
HelperInstance,
isClassicHelper,
SimpleHelper,
} from './helper';
import { default as componentAssertionHelper } from './helpers/-assert-implicit-component-helper-argument';
import { default as inElementNullCheckHelper } from './helpers/-in-element-null-check';
import { default as normalizeClassHelper } from './helpers/-normalize-class';
import { default as trackArray } from './helpers/-track-array';
Expand Down Expand Up @@ -193,7 +192,6 @@ const BUILTIN_KEYWORD_HELPERS = {
'-get-dynamic-var': internalHelper(getDynamicVar),
'-mount': mountHelper,
'-outlet': outletHelper,
'-assert-implicit-component-helper-argument': componentAssertionHelper,
'-in-el-null': inElementNullCheckHelper,
};

Expand Down Expand Up @@ -267,10 +265,10 @@ export default class ResolverImpl implements RuntimeResolver<Owner>, CompileTime
// we'll trigger an assertion
if (!CLASSIC_HELPER_MANAGER_ASSOCIATED.has(factory)) {
CLASSIC_HELPER_MANAGER_ASSOCIATED.add(factory);
setInternalHelperManager(CLASSIC_HELPER_MANAGER_FACTORY, factory);
setInternalHelperManager(CLASSIC_HELPER_MANAGER, factory);
}
} else {
setInternalHelperManager(CLASSIC_HELPER_MANAGER_FACTORY, factory);
setInternalHelperManager(CLASSIC_HELPER_MANAGER, factory);
}

return factory;
Expand Down Expand Up @@ -345,7 +343,7 @@ export default class ResolverImpl implements RuntimeResolver<Owner>, CompileTime
};
} else {
let factory = owner.factoryFor(P`component:-default`)!;
let manager = getInternalComponentManager(owner, factory.class as object);
let manager = getInternalComponentManager(factory.class as object);

definition = {
state: factory,
Expand All @@ -358,7 +356,7 @@ export default class ResolverImpl implements RuntimeResolver<Owner>, CompileTime

let factory = pair.component;
let ComponentClass = factory.class!;
let manager = getInternalComponentManager(owner, ComponentClass);
let manager = getInternalComponentManager(ComponentClass);

definition = {
state: isCurlyManager(manager) ? factory : ComponentClass,
Expand Down
Expand Up @@ -1319,20 +1319,6 @@ moduleFor(
this.assertStableRerender();
}

['@test GH#17121 implicit component invocations should not perform string lookup']() {
this.registerComponent('foo-bar', { template: 'foo-bar component' });

expectAssertion(
() =>
this.render(strip`
{{#let 'foo-bar' as |foo|}}
{{foo 1 2 3}}
{{/let}}
`),
"expected `foo` to be a contextual component but found a string. Did you mean `(component foo)`? ('-top-level' @ L1:C29) "
);
}

['@test RFC#311 invoking named args (without arguments)']() {
this.registerComponent('x-outer', { template: '{{@inner}}' });
this.registerComponent('x-inner', { template: 'inner' });
Expand Down
Expand Up @@ -19,6 +19,7 @@ import {
on,
fn,
} from '@ember/-internals/glimmer';
import GlimmerishComponent from '../../utils/glimmerish-component';

if (EMBER_STRICT_MODE) {
moduleFor(
Expand Down Expand Up @@ -89,6 +90,69 @@ if (EMBER_STRICT_MODE) {
this.assertHTML('foobar');
this.assertStableRerender();
}

'@test Can use a dynamic component definition'() {
let Foo = defineComponent({}, 'Hello, world!');
let Bar = defineComponent(
{},
'<this.Foo/>',
class extends GlimmerishComponent {
Foo = Foo;
}
);

this.registerComponent('bar', { ComponentClass: Bar });

this.render('<Bar/>');
this.assertHTML('Hello, world!');
this.assertStableRerender();
}

'@test Can use a dynamic component definition (curly)'() {
let Foo = defineComponent({}, 'Hello, world!');
let Bar = defineComponent(
{},
'{{this.Foo}}',
class extends GlimmerishComponent {
Foo = Foo;
}
);

this.registerComponent('bar', { ComponentClass: Bar });

this.render('<Bar/>');
this.assertHTML('Hello, world!');
this.assertStableRerender();
}

'@test Can use a dynamic helper definition'() {
let foo = defineSimpleHelper(() => 'Hello, world!');
let Bar = defineComponent(
{},
'{{this.foo}}',
class extends GlimmerishComponent {
foo = foo;
}
);

this.registerComponent('bar', { ComponentClass: Bar });

this.render('<Bar/>');
this.assertHTML('Hello, world!');
this.assertStableRerender();
}

'@feature(EMBER_DYNAMIC_HELPERS_AND_MODIFIERS) Can use a curried dynamic helper'() {
let foo = defineSimpleHelper((value) => value);
let Foo = defineComponent({}, '{{@value}}');
let Bar = defineComponent({ Foo, foo }, '<Foo @value={{foo "Hello, world!"}}/>');

this.registerComponent('bar', { ComponentClass: Bar });

this.render('<Bar/>');
this.assertHTML('Hello, world!');
this.assertStableRerender();
}
}
);

Expand Down
@@ -1,7 +1,13 @@
import { DEBUG } from '@glimmer/env';

import { RenderingTestCase, moduleFor, runDestroy, runTask } from 'internal-test-helpers';
import { Helper } from '@ember/-internals/glimmer';
import {
RenderingTestCase,
moduleFor,
runDestroy,
runTask,
defineSimpleHelper,
} from 'internal-test-helpers';
import { Helper, Component } from '@ember/-internals/glimmer';
import { set, tracked } from '@ember/-internals/metal';
import { backtrackingMessageFor } from '../../utils/debug-stack';

Expand Down Expand Up @@ -769,6 +775,31 @@ moduleFor(
this.render('{{hello-world}}');
}, expectedMessage);
}

'@feature(EMBER_DYNAMIC_HELPERS_AND_MODIFIERS) Can use a curried dynamic helper'() {
let val = defineSimpleHelper((value) => value);

this.registerComponent('foo', {
template: '{{@value}}',
});

this.registerComponent('bar', {
template: '<Foo @value={{helper this.val "Hello, world!"}}/>',
ComponentClass: Component.extend({ val }),
});

this.render('<Bar/>');
this.assertText('Hello, world!');
this.assertStableRerender();
}

'@feature(!EMBER_DYNAMIC_HELPERS_AND_MODIFIERS) Can use a curried dynamic helper'() {
expectAssertion(() => {
this.registerComponent('bar', {
template: '<Foo @value={{helper this.val "Hello, world!"}}/>',
});
}, /Cannot use the \(helper\) keyword yet, as it has not been implemented/);
}
}
);

Expand Down
Expand Up @@ -19,7 +19,7 @@ moduleFor(
getOnManagerInstance() {
// leveraging private APIs, this can be deleted if these APIs change
// but it has been useful to verify some internal details
return getInternalModifierManager(this.owner, on);
return getInternalModifierManager(on);
}

assertCounts(expected) {
Expand Down Expand Up @@ -390,7 +390,7 @@ moduleFor(
getOnManagerInstance() {
// leveraging private APIs, this can be deleted if these APIs change
// but it has been useful to verify some internal details
return getInternalModifierManager(this.owner, on);
return getInternalModifierManager(on);
}

assertCounts(expected) {
Expand Down
4 changes: 4 additions & 0 deletions packages/@ember/canary-features/index.ts
Expand Up @@ -20,6 +20,7 @@ export const DEFAULT_FEATURES = {
EMBER_GLIMMER_INVOKE_HELPER: true,
EMBER_MODERNIZED_BUILT_IN_COMPONENTS: null,
EMBER_STRICT_MODE: null,
EMBER_DYNAMIC_HELPERS_AND_MODIFIERS: null,
};

/**
Expand Down Expand Up @@ -77,3 +78,6 @@ export const EMBER_MODERNIZED_BUILT_IN_COMPONENTS = featureValue(
FEATURES.EMBER_MODERNIZED_BUILT_IN_COMPONENTS
);
export const EMBER_STRICT_MODE = featureValue(FEATURES.EMBER_STRICT_MODE);
export const EMBER_DYNAMIC_HELPERS_AND_MODIFIERS = featureValue(
FEATURES.EMBER_DYNAMIC_HELPERS_AND_MODIFIERS
);

0 comments on commit 7e14be6

Please sign in to comment.