Skip to content

Commit

Permalink
Add build assertion against {{outlet named}}
Browse files Browse the repository at this point in the history
Named outlets are effectively removed in Ember 4.0, as the render hook
and `renderTemplate` method which are used to configure them have been
removed. Add an assertion to catch anyone trying to use this API going
forward.
  • Loading branch information
mixonic committed Aug 1, 2021
1 parent 015bd42 commit 392981c
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 140 deletions.
140 changes: 0 additions & 140 deletions packages/@ember/-internals/glimmer/tests/integration/outlet-test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { RenderingTestCase, moduleFor, runAppend, runTask } from 'internal-test-helpers';

import { set } from '@ember/-internals/metal';

moduleFor(
'outlet view',
class extends RenderingTestCase {
Expand Down Expand Up @@ -131,144 +129,6 @@ moduleFor(
this.assertText('HIBYE');
}

['@test should support an optional name']() {
this.registerTemplate('application', '<h1>HI</h1>{{outlet "special"}}');
let outletState = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'application',
controller: {},
template: this.owner.lookup('template:application')(this.owner),
},
outlets: Object.create(null),
};

runTask(() => this.component.setOutletState(outletState));

runAppend(this.component);

this.assertText('HI');

this.assertStableRerender();

this.registerTemplate('special', '<p>BYE</p>');
outletState.outlets.special = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'special',
controller: {},
template: this.owner.lookup('template:special')(this.owner),
},
outlets: Object.create(null),
};

runTask(() => this.component.setOutletState(outletState));

this.assertText('HIBYE');
}

['@test does not default outlet name when positional argument is present']() {
this.registerTemplate('application', '<h1>HI</h1>{{outlet this.someUndefinedThing}}');
let outletState = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'application',
controller: {},
template: this.owner.lookup('template:application')(this.owner),
},
outlets: Object.create(null),
};

runTask(() => this.component.setOutletState(outletState));

runAppend(this.component);

this.assertText('HI');

this.assertStableRerender();

this.registerTemplate('special', '<p>BYE</p>');
outletState.outlets.main = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'special',
controller: {},
template: this.owner.lookup('template:special')(this.owner),
},
outlets: Object.create(null),
};

runTask(() => this.component.setOutletState(outletState));

this.assertText('HI');
}

['@test should support bound outlet name']() {
let controller = { outletName: 'foo' };
this.registerTemplate('application', '<h1>HI</h1>{{outlet this.outletName}}');
let outletState = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'application',
controller,
template: this.owner.lookup('template:application')(this.owner),
},
outlets: Object.create(null),
};

runTask(() => this.component.setOutletState(outletState));

runAppend(this.component);

this.assertText('HI');

this.assertStableRerender();

this.registerTemplate('foo', '<p>FOO</p>');
outletState.outlets.foo = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'foo',
controller: {},
template: this.owner.lookup('template:foo')(this.owner),
},
outlets: Object.create(null),
};

this.registerTemplate('bar', '<p>BAR</p>');
outletState.outlets.bar = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'bar',
controller: {},
template: this.owner.lookup('template:bar')(this.owner),
},
outlets: Object.create(null),
};

runTask(() => this.component.setOutletState(outletState));

this.assertText('HIFOO');

runTask(() => set(controller, 'outletName', 'bar'));

this.assertText('HIBAR');
}

['@test outletState can pass through user code (liquid-fire initimate API) ']() {
this.registerTemplate(
'outer',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { assert } from '@ember/debug';
import { AST, ASTPlugin } from '@glimmer/syntax';
import calculateLocationDisplay from '../system/calculate-location-display';
import { EmberASTPluginEnvironment } from '../types';

/**
@module ember
*/

/**
Prevents usage of named outlets, a legacy concept in Ember removed in 4.0.
@private
@class AssertAgainstNamedOutlets
*/
export default function assertAgainstNamedOutlets(env: EmberASTPluginEnvironment): ASTPlugin {
let moduleName = env.meta?.moduleName;

return {
name: 'assert-against-named-outlets',

visitor: {
MustacheStatement(node: AST.MustacheStatement) {
if (
node.path.type === 'PathExpression' &&
node.path.original === 'outlet' &&
node.params[0]
) {
let sourceInformation = calculateLocationDisplay(moduleName, node.loc);
assert(
`Named outlets were removed in Ember 4.0. See https://deprecations.emberjs.com/v3.x#toc_route-render-template for guidance on alternative APIs for named outlet use cases. ${sourceInformation}`
);
}
},
},
};
}
3 changes: 3 additions & 0 deletions packages/ember-template-compiler/lib/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import AssertAgainstDynamicHelpersModifiers from './assert-against-dynamic-helpers-modifiers';
import AssertAgainstNamedBlocks from './assert-against-named-blocks';
import AssertAgainstNamedOutlets from './assert-against-named-outlets';
import AssertInputHelperWithoutBlock from './assert-input-helper-without-block';
import AssertReservedNamedArguments from './assert-reserved-named-arguments';
import AssertSplattributeExpressions from './assert-splattribute-expression';
Expand Down Expand Up @@ -30,6 +31,7 @@ export const RESOLUTION_MODE_TRANSFORMS = Object.freeze(
TransformInElement,
AssertSplattributeExpressions,
TransformEachTrackArray,
AssertAgainstNamedOutlets,
TransformWrapMountAndOutlet,
!EMBER_NAMED_BLOCKS ? AssertAgainstNamedBlocks : null,
EMBER_DYNAMIC_HELPERS_AND_MODIFIERS
Expand All @@ -47,6 +49,7 @@ export const STRICT_MODE_TRANSFORMS = Object.freeze(
TransformInElement,
AssertSplattributeExpressions,
TransformEachTrackArray,
AssertAgainstNamedOutlets,
TransformWrapMountAndOutlet,
!EMBER_NAMED_BLOCKS ? AssertAgainstNamedBlocks : null,
!EMBER_DYNAMIC_HELPERS_AND_MODIFIERS ? AssertAgainstDynamicHelpersModifiers : null,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { compile } from '../../index';
import { moduleFor, AbstractTestCase } from 'internal-test-helpers';

moduleFor(
'ember-template-compiler: assert-against-named-outlets',
class extends AbstractTestCase {
[`named outlets are asserted against`]() {
expectAssertion(() => {
compile(`{{outlet 'foo'}}`, {
moduleName: 'baz/foo-bar',
});
}, `Named outlets were removed in Ember 4.0. See https://deprecations.emberjs.com/v3.x#toc_route-render-template for guidance on alternative APIs for named outlet use cases. ('baz/foo-bar' @ L1:C5) `);

expectAssertion(() => {
compile(`{{outlet foo}}`, {
moduleName: 'baz/foo-bar',
});
}, `Named outlets were removed in Ember 4.0. See https://deprecations.emberjs.com/v3.x#toc_route-render-template for guidance on alternative APIs for named outlet use cases. ('baz/foo-bar' @ L1:C5) `);

// No assertion
compile(`{{outlet}}`, {
moduleName: 'baz/foo-bar',
});
}
}
);

0 comments on commit 392981c

Please sign in to comment.