Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 58 additions & 24 deletions packages/ember-glimmer/lib/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,43 @@ export default class Environment extends GlimmerEnvironment {
};
}

// Hello future traveler, welcome to the world of syntax refinement.
// The method below is called by Glimmer's runtime compiler to allow
// us to take generic statement syntax and refine it to more meaniful
// syntax for Ember's use case. This on the fly switch-a-roo sounds fine
// and dandy, however Ember has precedence on statement refinement that you
// need to be aware of. The presendence for language constructs is as follows:
//
// ------------------------
// Native & Built-in Syntax
// ------------------------
// User-land components
// ------------------------
// User-land helpers
// ------------------------
//
// The one caveat here is that Ember also allows for dashed references that are
// not a component or helper:
//
// export default Component.extend({
// 'foo-bar': 'LAME'
// });
//
// {{foo-bar}}
//
// The heuristic for the above situation is a dashed "key" in inline form
// that does not resolve to a defintion. In this case refine statement simply
// isn't going to return any syntax and the Glimmer engine knows how to handle
// this case.

refineStatement(statement) {
// 1. resolve any native syntax – if, unless, with, each, and partial
let nativeSyntax = super.refineStatement(statement);

if (nativeSyntax) {
return nativeSyntax;
}

let {
isSimple,
isInline,
Expand All @@ -133,37 +169,37 @@ export default class Environment extends GlimmerEnvironment {
templates
} = statement;

if (key !== 'partial' && isSimple && (isInline || isBlock)) {
assert(`You attempted to overwrite the built-in helper "${key}" which is not allowed. Please rename the helper.`, !(builtInHelpers[key] && this.owner.hasRegistration(`helper:${key}`)));

if (isSimple && (isInline || isBlock)) {
// 2. built-in syntax
if (key === 'component') {
return new DynamicComponentSyntax({ args, templates });
} else if (key === 'outlet') {
return new OutletSyntax({ args });
}

let internalKey = builtInComponents[key];
let definition = null;

if (internalKey) {
definition = this.getComponentDefinition([internalKey]);
} else if (key.indexOf('-') >= 0) {
let definition = this.getComponentDefinition(path);

if (definition) {
wrapClassBindingAttribute(args);
wrapClassAttribute(args);
return new CurlyComponentSyntax({ args, definition, templates });
} else if (isBlock && !this.hasHelper(key)) {
assert(`A helper named '${path[0]}' could not be found`, false);
}
} else {
// Check if it's a keyword
let mappedKey = builtInComponents[key];
if (mappedKey) {
let definition = this.getComponentDefinition([mappedKey]);
wrapClassBindingAttribute(args);
wrapClassAttribute(args);
return new CurlyComponentSyntax({ args, definition, templates });
}
definition = this.getComponentDefinition(path);
}

if (definition) {
wrapClassBindingAttribute(args);
wrapClassAttribute(args);
return new CurlyComponentSyntax({ args, definition, templates });
}

assert(`Could not find component named "${key}" (no component or template with that name was found)`, !isBlock || this.hasHelper(key));
}

let nativeSyntax = super.refineStatement(statement);
assert(`Helpers may not be used in the block form, for example {{#${key}}}{{/${key}}}. Please use a component, or alternatively use the helper in combination with a built-in Ember helper, for example {{#if (${key})}}{{/if}}.`, !nativeSyntax && key && this.hasHelper(key) ? !isBlock : true);
assert(`Helpers may not be used in the block form, for example {{#${key}}}{{/${key}}}. Please use a component, or alternatively use the helper in combination with a built-in Ember helper, for example {{#if (${key})}}{{/if}}.`, !isBlock || !this.hasHelper(key));

assert(`Helpers may not be used in the element form.`, !nativeSyntax && key && this.hasHelper(key) ? !isModifier : true);
return nativeSyntax;
}

hasComponentDefinition() {
Expand All @@ -179,8 +215,6 @@ export default class Environment extends GlimmerEnvironment {

if (ComponentClass || layout) {
definition = this._components[name] = new CurlyComponentDefinition(name, ComponentClass, layout);
} else if (!this.hasHelper(name)) {
assert(`Glimmer error: Could not find component named "${name}" (no component or template with that name was found)`, !!(ComponentClass || layout));
}
}

Expand Down
6 changes: 6 additions & 0 deletions packages/ember-glimmer/lib/syntax/dynamic-component.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ArgsSyntax, StatementSyntax } from 'glimmer-runtime';
import { ConstReference, isConst, UNDEFINED_REFERENCE } from 'glimmer-reference';
import { assert } from 'ember-metal/debug';

function dynamicComponentFor(vm) {
let env = vm.env;
Expand All @@ -10,6 +11,8 @@ function dynamicComponentFor(vm) {
let name = nameRef.value();
let definition = env.getComponentDefinition([name]);

assert(`Could not find component named "${name}" (no component or template with that name was found)`, definition);

return new ConstReference(definition);
} else {
return new DynamicComponentReference({ nameRef, env });
Expand Down Expand Up @@ -44,6 +47,9 @@ class DynamicComponentReference {

if (typeof name === 'string') {
let definition = env.getComponentDefinition([name]);

assert(`Could not find component named "${name}" (no component or template with that name was found)`, definition);

return definition;
} else {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,7 @@ moduleFor('Components test: curly components', class extends RenderingTest {

this.assertComponentElement(this.firstChild, { content: 'true' });
}
['@htmlbars lookup of component takes priority over property']() {
['@test lookup of component takes priority over property']() {
this.registerComponent('some-component', {
template: 'some-component'
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ let assert = QUnit.assert;

moduleFor('Helpers test: custom helpers', class extends RenderingTest {

['@glimmer it cannot override built-in syntax']() {
this.registerHelper('if', () => 'Nope');
expectAssertion(() => {
this.render(`{{if foo 'LOL'}}`, { foo: true });
}, /You attempted to overwrite the built-in helper \"if\" which is not allowed. Please rename the helper./);
}

['@test it can resolve custom simple helpers with or without dashes']() {
this.registerHelper('hello', () => 'hello');
this.registerHelper('hello-world', () => 'hello world');
Expand Down
12 changes: 0 additions & 12 deletions packages/ember/tests/component_registration_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,18 +144,6 @@ QUnit.test('Late-registered components can be rendered with ONLY the template re
ok(!helpers['borf-snorlax'], 'Component wasn\'t saved to global helpers hash');
});

test('Component-like invocations are treated as bound paths if neither template nor component are registered on the container', function() {
setTemplate('application', compile('<div id=\'wrapper\'>{{user-name}} hello {{api-key}} world</div>'));

boot(() => {
appInstance.register('controller:application', Controller.extend({
'user-name': 'machty'
}));
});

equal(jQuery('#wrapper').text(), 'machty hello world', 'The component is composed correctly');
});

QUnit.test('Assigning layoutName to a component should setup the template as a layout', function() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicated test.

expect(1);

Expand Down