Skip to content

Commit

Permalink
Deprecate {{render}} helper and outlet orphaning
Browse files Browse the repository at this point in the history
  • Loading branch information
josemarluedke committed Oct 15, 2016
1 parent 45f1416 commit 9b608cd
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 23 deletions.
47 changes: 33 additions & 14 deletions packages/ember-glimmer/tests/integration/helpers/render-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ moduleFor('Helpers test: {{render}}', class extends RenderingTest {
this.owner.register('controller:home', Controller.extend());
this.registerTemplate('home', '<p>BYE</p>');

this.render(`<h1>HI</h1>{{render 'home'}}`);
expectDeprecation(() => {
this.render(`<h1>HI</h1>{{render 'home'}}`);
}, /Please refactor [\w\{\}"` ]+ to a component/);

this.assertText('HIBYE');
}
Expand All @@ -19,20 +21,25 @@ moduleFor('Helpers test: {{render}}', class extends RenderingTest {
this.owner.register('controller:baz', Controller.extend());

this.registerTemplate('home', '<p>BYE</p>');
this.registerTemplate('foo', `<p>FOO</p>{{render 'bar'}}`);
this.registerTemplate('bar', `<p>BAR</p>{{render 'baz'}}`);
this.registerTemplate('baz', `<p>BAZ</p>`);

this.render('<h1>HI</h1>{{render \'foo\'}}');
expectDeprecation(() => {
this.registerTemplate('foo', `<p>FOO</p>{{render 'bar'}}`);
this.registerTemplate('bar', `<p>BAR</p>{{render 'baz'}}`);
this.render('<h1>HI</h1>{{render \'foo\'}}');
}, /Please refactor [\w\{\}"` ]+ to a component/);

this.assertText('HIFOOBARBAZ');
}

['@test should have assertion if the template does not exist']() {
this.owner.register('controller:oops', Controller.extend());

expectAssertion(() => {
this.render(`<h1>HI</h1>{{render 'oops'}}`);
}, 'You used `{{render \'oops\'}}`, but \'oops\' can not be found as a template.');
expectDeprecation(() => {
expectAssertion(() => {
this.render(`<h1>HI</h1>{{render 'oops'}}`);
}, 'You used `{{render \'oops\'}}`, but \'oops\' can not be found as a template.');
}, /Please refactor [\w\{\}"` ]+ to a component/);
}

['@test should render given template with the singleton controller as its context']() {
Expand All @@ -43,7 +50,9 @@ moduleFor('Helpers test: {{render}}', class extends RenderingTest {
}));
this.registerTemplate('post', '<p>{{title}}</p>');

this.render(`<h1>HI</h1>{{render 'post'}}`);
expectDeprecation(() => {
this.render(`<h1>HI</h1>{{render 'post'}}`);
}, /Please refactor [\w\{\}"` ]+ to a component/);

this.assertText(`HIIt's Simple Made Easy`);

Expand Down Expand Up @@ -78,7 +87,9 @@ moduleFor('Helpers test: {{render}}', class extends RenderingTest {

this.registerTemplate('post', '<p>{{title}}</p>');

this.render(`{{#if showPost}}{{render 'post'}}{{else}}Nothing here{{/if}}`, { showPost: false });
expectDeprecation(() => {
this.render(`{{#if showPost}}{{render 'post'}}{{else}}Nothing here{{/if}}`, { showPost: false });
}, /Please refactor [\w\{\}"` ]+ to a component/);

this.assertText(`Nothing here`);

Expand Down Expand Up @@ -212,9 +223,12 @@ moduleFor('Helpers test: {{render}}', class extends RenderingTest {
['@test should raise an error when a given controller name does not resolve to a controller']() {
this.registerTemplate('home', '<p>BYE</p>');
this.owner.register('controller:posts', Controller.extend());
expectAssertion(() => {
this.render(`<h1>HI</h1>{{render "home" controller="postss"}}`);
}, /The controller name you supplied \'postss\' did not resolve to a controller./);

expectDeprecation(() => {
expectAssertion(() => {
this.render(`<h1>HI</h1>{{render "home" controller="postss"}}`);
}, /The controller name you supplied \'postss\' did not resolve to a controller./);
}, /Please refactor [\w\{\}"` ]+ to a component/);
}

['@test should render with given controller'](assert) {
Expand All @@ -231,7 +245,10 @@ moduleFor('Helpers test: {{render}}', class extends RenderingTest {
}
}));

this.render('{{render "home" controller="posts"}}');
expectDeprecation(() => {
this.render('{{render "home" controller="posts"}}');
}, /Please refactor [\w\{\}"` ]+ to a component/);

let renderedController = this.owner.lookup('controller:posts');
let uniqueId = renderedController.get('uniqueId');
let renderedModel = renderedController.get('model');
Expand Down Expand Up @@ -331,7 +348,9 @@ moduleFor('Helpers test: {{render}}', class extends RenderingTest {
}
}));

this.render('{{render "blog.post"}}');
expectDeprecation(() => {
this.render('{{render "blog.post"}}');
}, /Please refactor [\w\.{\}"` ]+ to a component/);

this.assertText(`0`);
}
Expand Down
13 changes: 12 additions & 1 deletion packages/ember-routing/lib/system/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import {
set,
defineProperty,
computed,
run
run,
deprecate
} from 'ember-metal';
import {
Object as EmberObject,
Expand Down Expand Up @@ -1311,6 +1312,16 @@ function appendLiveRoute(liveRoutes, defaultParentState, renderOptions) {
set(target.outlets, renderOptions.outlet, myState);
} else {
if (renderOptions.into) {
deprecate(
`Rendering into a {{render}} helper that resolves to an {{outlet}} is deprecated.`,
false,
{
id: 'ember-routing.top-level-render-helper',
until: '3.0.0',
url: 'http://emberjs.com/deprecations/v2.x/#toc_rendering-into-a-render-helper-that-resolves-to-an-outlet'
}
);

// Megahax time. Post-3.0-breaking-changes, we will just assert
// right here that the user tried to target a nonexistent
// thing. But for now we still need to support the `render`
Expand Down
51 changes: 51 additions & 0 deletions packages/ember-template-compiler/lib/plugins/deprecate-render.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { deprecate } from 'ember-metal';
import calculateLocationDisplay from '../system/calculate-location-display';

export default function DeprecateRender(options) {
this.syntax = null;
this.options = options;
}

DeprecateRender.prototype.transform =
function DeprecateRender_transform(ast) {
let moduleName = this.options.meta.moduleName;
let walker = new this.syntax.Walker();

walker.visit(ast, function(node) {
if (!validate(node)) { return; }

each(node.params, (param) => {
if (param.type !== 'StringLiteral') { return; }

deprecate(deprecationMessage(moduleName, node), false, {
id: 'ember-template-compiler.deprecate-render',
until: '3.0.0',
url: 'http://emberjs.com/deprecations/v2.x#toc_code-render-code-helper'
});
});
});

return ast;
};

function validate(node) {
return (node.type === 'MustacheStatement') &&
(node.path.original === 'render') &&
(node.params.length === 1);
}

function each(list, callback) {
for (let i = 0, l = list.length; i < l; i++) {
callback(list[i]);
}
}

function deprecationMessage(moduleName, node) {
let sourceInformation = calculateLocationDisplay(moduleName, node.loc);
let componentName = node.params[0].original;
let original = `{{render "${componentName}"}}`;
let preferred = `{{${componentName}}}`;

return `Please refactor \`${original}\` to a component and invoke via` +
` \`${preferred}\`. ${sourceInformation}`;
}
2 changes: 2 additions & 0 deletions packages/ember-template-compiler/lib/plugins/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import TransformTopLevelComponents from './transform-top-level-components';
import TransformInlineLinkTo from './transform-inline-link-to';
import TransformOldClassBindingSyntax from './transform-old-class-binding-syntax';
import DeprecateRenderModel from './deprecate-render-model';
import DeprecateRender from './deprecate-render';
import AssertReservedNamedArguments from './assert-reserved-named-arguments';
import TransformActionSyntax from './transform-action-syntax';
import TransformInputTypeSyntax from './transform-input-type-syntax';
Expand All @@ -22,6 +23,7 @@ export default Object.freeze([
TransformInlineLinkTo,
TransformOldClassBindingSyntax,
DeprecateRenderModel,
DeprecateRender,
AssertReservedNamedArguments,
TransformActionSyntax,
TransformInputTypeSyntax,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { compile } from '../../index';

QUnit.module('ember-template-compiler: deprecate-render');

QUnit.test('Using `{{render` without a model provides a deprecation', function() {
expect(1);

let expectedMessage =
`Please refactor \`{{render "foo-bar"}}\` to a component and` +
` invoke via \`{{foo-bar}}\`. ('baz/foo-bar' @ L1:C0) `;

expectDeprecation(() => {
compile('{{render "foo-bar"}}', {
moduleName: 'baz/foo-bar'
});
}, expectedMessage);
});
54 changes: 46 additions & 8 deletions packages/ember/tests/routing/basic_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2290,6 +2290,8 @@ QUnit.test('Route should tear down multiple outlets', function() {


QUnit.test('Route will assert if you try to explicitly render {into: ...} a missing template', function () {
expectDeprecation(/Rendering into a {{render}} helper that resolves to an {{outlet}} is deprecated./);

Router.map(function() {
this.route('home', { path: '/' });
});
Expand Down Expand Up @@ -3441,7 +3443,12 @@ QUnit.test('Allows any route to disconnectOutlet another route\'s templates', fu
});

QUnit.test('Can this.render({into:...}) the render helper', function() {
setTemplate('application', compile('{{render "sidebar"}}'));
expectDeprecation(/Rendering into a {{render}} helper that resolves to an {{outlet}} is deprecated./);

expectDeprecation(() => {
setTemplate('application', compile('{{render "sidebar"}}'));
}, /Please refactor [\w\{\}"` ]+ to a component/);

setTemplate('sidebar', compile('<div class="sidebar">{{outlet}}</div>'));
setTemplate('index', compile('other'));
setTemplate('bar', compile('bar'));
Expand All @@ -3468,7 +3475,12 @@ QUnit.test('Can this.render({into:...}) the render helper', function() {
});

QUnit.test('Can disconnect from the render helper', function() {
setTemplate('application', compile('{{render "sidebar"}}'));
expectDeprecation(/Rendering into a {{render}} helper that resolves to an {{outlet}} is deprecated./);

expectDeprecation(() => {
setTemplate('application', compile('{{render "sidebar"}}'));
}, /Please refactor [\w\{\}"` ]+ to a component/);

setTemplate('sidebar', compile('<div class="sidebar">{{outlet}}</div>'));
setTemplate('index', compile('other'));

Expand All @@ -3493,7 +3505,12 @@ QUnit.test('Can disconnect from the render helper', function() {
});

QUnit.test('Can this.render({into:...}) the render helper\'s children', function() {
setTemplate('application', compile('{{render "sidebar"}}'));
expectDeprecation(/Rendering into a {{render}} helper that resolves to an {{outlet}} is deprecated./);

expectDeprecation(() => {
setTemplate('application', compile('{{render "sidebar"}}'));
}, /Please refactor [\w\{\}"` ]+ to a component/);

setTemplate('sidebar', compile('<div class="sidebar">{{outlet}}</div>'));
setTemplate('index', compile('<div class="index">{{outlet}}</div>'));
setTemplate('other', compile('other'));
Expand Down Expand Up @@ -3522,7 +3539,12 @@ QUnit.test('Can this.render({into:...}) the render helper\'s children', function
});

QUnit.test('Can disconnect from the render helper\'s children', function() {
setTemplate('application', compile('{{render "sidebar"}}'));
expectDeprecation(/Rendering into a {{render}} helper that resolves to an {{outlet}} is deprecated./);

expectDeprecation(() => {
setTemplate('application', compile('{{render "sidebar"}}'));
}, /Please refactor [\w\{\}"` ]+ to a component/);

setTemplate('sidebar', compile('<div class="sidebar">{{outlet}}</div>'));
setTemplate('index', compile('<div class="index">{{outlet}}</div>'));
setTemplate('other', compile('other'));
Expand All @@ -3549,8 +3571,16 @@ QUnit.test('Can disconnect from the render helper\'s children', function() {
});

QUnit.test('Can this.render({into:...}) nested render helpers', function() {
setTemplate('application', compile('{{render "sidebar"}}'));
setTemplate('sidebar', compile('<div class="sidebar">{{render "cart"}}</div>'));
expectDeprecation(/Rendering into a {{render}} helper that resolves to an {{outlet}} is deprecated./);

expectDeprecation(() => {
setTemplate('application', compile('{{render "sidebar"}}'));
}, /Please refactor [\w\{\}"` ]+ to a component/);

expectDeprecation(() => {
setTemplate('sidebar', compile('<div class="sidebar">{{render "cart"}}</div>'));
}, /Please refactor [\w\{\}"` ]+ to a component/);

setTemplate('cart', compile('<div class="cart">{{outlet}}</div>'));
setTemplate('index', compile('other'));
setTemplate('baz', compile('baz'));
Expand All @@ -3577,8 +3607,16 @@ QUnit.test('Can this.render({into:...}) nested render helpers', function() {
});

QUnit.test('Can disconnect from nested render helpers', function() {
setTemplate('application', compile('{{render "sidebar"}}'));
setTemplate('sidebar', compile('<div class="sidebar">{{render "cart"}}</div>'));
expectDeprecation(/Rendering into a {{render}} helper that resolves to an {{outlet}} is deprecated./);

expectDeprecation(() => {
setTemplate('application', compile('{{render "sidebar"}}'));
}, /Please refactor [\w\{\}"` ]+ to a component/);

expectDeprecation(() => {
setTemplate('sidebar', compile('<div class="sidebar">{{render "cart"}}</div>'));
}, /Please refactor [\w\{\}"` ]+ to a component/);

setTemplate('cart', compile('<div class="cart">{{outlet}}</div>'));
setTemplate('index', compile('other'));

Expand Down

0 comments on commit 9b608cd

Please sign in to comment.