Skip to content

Commit

Permalink
allow null, undefined, if and unless for strict component arg…
Browse files Browse the repository at this point in the history
…uments
  • Loading branch information
mydea committed Aug 17, 2021
1 parent 28460bf commit 6e34a6a
Show file tree
Hide file tree
Showing 2 changed files with 222 additions and 1 deletion.
34 changes: 33 additions & 1 deletion packages/compat/src/resolver-transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export function makeResolverTransform(resolver: Resolver) {
for (let name of argumentsAreComponents) {
let attr = node.attributes.find((attr: any) => attr.name === '@' + name);
if (attr) {
handleComponentHelper(attr.value, resolver, filename, scopeStack, {
handleOptionalComponentHelper(attr.value, resolver, filename, scopeStack, {
componentName: node.tag,
argumentName: name,
});
Expand Down Expand Up @@ -316,3 +316,35 @@ function handleComponentHelper(

// resolver.unresolvableComponentArgument(componentName, argumentName, moduleName, param.loc);
}

function handleOptionalComponentHelper(
param: any,
resolver: Resolver,
moduleName: string,
scopeStack: ScopeStack,
impliedBecause?: { componentName: string; argumentName: string }
): void {
switch (param.type) {
case 'NullLiteral':
case 'UndefinedLiteral':
return;

case 'MustacheStatement':
case 'SubExpression':
if (param.path.type === 'NullLiteral' || param.path.type === 'UndefinedLiteral') {
return;
}

// We specifically allow `{{if}}` and `{{unless}}` as long as both branches are valid
if (param.path.type === 'PathExpression' && (param.path.original === 'if' || param.path.original === 'unless')) {
// Ignore first one, which is the truthy check
param.params.slice(1).forEach((param: any) => {
handleOptionalComponentHelper(param, resolver, moduleName, scopeStack, impliedBecause);
});
break;
}

default:
handleComponentHelper(param, resolver, moduleName, scopeStack, impliedBecause);
}
}
189 changes: 189 additions & 0 deletions packages/compat/tests/resolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1297,6 +1297,161 @@ describe('compat-resolver', function () {
]);
});

test('allow to pass `null` as argument for component', function () {
let packageRules: PackageRules[] = [
{
package: 'the-test-package',
components: {
'<FormBuilder />': {
yieldsArguments: ['navbar'],
},
},
},
];
let findDependencies = configure({ staticComponents: true, packageRules });
givenFile('templates/components/form-builder.hbs');

expect(
findDependencies(
'templates/components/x.hbs',
`
<FormBuilder @navbar={{null}} as |bar|>
{{component bar}}
</FormBuilder>
`
)
).toEqual([
{
path: './form-builder.hbs',
runtimeName: 'the-app/templates/components/form-builder',
},
]);
});

test('allow to pass `undefined` as argument for component', function () {
let packageRules: PackageRules[] = [
{
package: 'the-test-package',
components: {
'<FormBuilder />': {
yieldsArguments: ['navbar'],
},
},
},
];
let findDependencies = configure({ staticComponents: true, packageRules });
givenFile('templates/components/form-builder.hbs');

expect(
findDependencies(
'templates/components/x.hbs',
`
<FormBuilder @navbar={{undefined}} as |bar|>
{{component bar}}
</FormBuilder>
`
)
).toEqual([
{
path: './form-builder.hbs',
runtimeName: 'the-app/templates/components/form-builder',
},
]);
});

test('allow to pass `if` as argument for component', function () {
let packageRules: PackageRules[] = [
{
package: 'the-test-package',
components: {
'<FormBuilder />': {
yieldsArguments: ['navbar'],
},
},
},
];
let findDependencies = configure({ staticComponents: true, packageRules });
givenFile('templates/components/form-builder.hbs');

expect(
findDependencies(
'templates/components/x.hbs',
`
<FormBuilder @navbar={{if @navbarComponent (ensure-safe-component @navbar) null}} as |bar|>
{{component bar}}
</FormBuilder>
`
)
).toEqual([
{
path: './form-builder.hbs',
runtimeName: 'the-app/templates/components/form-builder',
},
]);
});

test('allow to pass nested `if` as argument for component', function () {
let packageRules: PackageRules[] = [
{
package: 'the-test-package',
components: {
'<FormBuilder />': {
yieldsArguments: ['navbar'],
},
},
},
];
let findDependencies = configure({ staticComponents: true, packageRules });
givenFile('templates/components/form-builder.hbs');

expect(
findDependencies(
'templates/components/x.hbs',
`
<FormBuilder @navbar={{if @navbarComponent (ensure-safe-component @navbar) (if @defaultNull null undefined)}} as |bar|>
{{component bar}}
</FormBuilder>
`
)
).toEqual([
{
path: './form-builder.hbs',
runtimeName: 'the-app/templates/components/form-builder',
},
]);
});

test('allow to pass `unless` as argument for component', function () {
let packageRules: PackageRules[] = [
{
package: 'the-test-package',
components: {
'<FormBuilder />': {
yieldsArguments: ['navbar'],
},
},
},
];
let findDependencies = configure({ staticComponents: true, packageRules });
givenFile('templates/components/form-builder.hbs');

expect(
findDependencies(
'templates/components/x.hbs',
`
<FormBuilder @navbar={{unless @navbar (ensure-safe-component @navbar) null}} as |bar|>
{{component bar}}
</FormBuilder>
`
)
).toEqual([
{
path: './form-builder.hbs',
runtimeName: 'the-app/templates/components/form-builder',
},
]);
});

test('yieldsArguments causes warning to propagate up lexically, angle', function () {
let packageRules: PackageRules[] = [
{
Expand Down Expand Up @@ -1331,6 +1486,40 @@ describe('compat-resolver', function () {
);
});

test('handle warnings inside of `if` for components', function () {
let packageRules: PackageRules[] = [
{
package: 'the-test-package',
components: {
'<FormBuilder />': {
yieldsArguments: ['navbar'],
},
},
},
];
let findDependencies = configure({ staticComponents: true, packageRules });
givenFile('templates/components/form-builder.hbs');
expect(() => {
expect(
findDependencies(
'templates/components/x.hbs',
`
<FormBuilder @navbar={{if true this.unknown}} as |bar|>
{{component bar}}
</FormBuilder>
`
)
).toEqual([
{
path: './form-builder.hbs',
runtimeName: 'the-app/templates/components/form-builder',
},
]);
}).toThrow(
/argument "navbar" to component "FormBuilder" is treated as a component, but the value you're passing is dynamic: this\.unknown/
);
});

test('yieldsArguments causes warning to propagate up lexically, curl', function () {
let packageRules: PackageRules[] = [
{
Expand Down

0 comments on commit 6e34a6a

Please sign in to comment.