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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ rules in templates can be disabled with eslint directives with mustache or html
| [template-no-obsolete-elements](docs/rules/template-no-obsolete-elements.md) | disallow obsolete HTML elements | | | |
| [template-no-outlet-outside-routes](docs/rules/template-no-outlet-outside-routes.md) | disallow {{outlet}} outside of route templates | | | |
| [template-no-page-title-component](docs/rules/template-no-page-title-component.md) | disallow usage of ember-page-title component | | | |
| [template-no-potential-path-strings](docs/rules/template-no-potential-path-strings.md) | disallow potential path strings in attribute values | | | |
| [template-no-splattributes-with-class](docs/rules/template-no-splattributes-with-class.md) | disallow splattributes with class attribute | | | |
| [template-no-trailing-spaces](docs/rules/template-no-trailing-spaces.md) | disallow trailing whitespace at the end of lines in templates | | 🔧 | |
| [template-no-unavailable-this](docs/rules/template-no-unavailable-this.md) | disallow `this` in templates that are not inside a class or function | | | |
Expand Down
41 changes: 41 additions & 0 deletions docs/rules/template-no-potential-path-strings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# ember/template-no-potential-path-strings

<!-- end auto-generated rule header -->

It might happen sometimes that `{{` and `}}` are forgotten when invoking a component, and the string that is passed was actually supposed to be a property path or argument.

This rule warns about all arguments and attributes that start with `this.` or `@`, but are missing the surrounding `{{` and `}}` characters.

## Examples

This rule **forbids** the following:

```hbs
<img src='this.picture' />
```

```hbs
<img src='@img' />
```

This rule **allows** the following:

```hbs
<img src={{this.picture}} />
```

```hbs
<img src={{@img}} />
```

## Migration

- Replace the surrounding `"` characters with `{{`/`}}`

## Related Rules

- [no-arguments-for-html-elements](template-no-arguments-for-html-elements.md)

## References

- [Component Arguments and HTML Attributes](https://guides.emberjs.com/release/components/component-arguments-and-html-attributes/)
47 changes: 47 additions & 0 deletions lib/rules/template-no-potential-path-strings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const FINE_SYMBOLS = ['|', '/', '\\'];

/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'disallow potential path strings in attribute values',
category: 'Best Practices',
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-potential-path-strings.md',
templateMode: 'both',
},
fixable: null,
schema: [],
messages: {
noPotentialPathStrings:
'Potential path in attribute string detected. Did you mean {{{{path}}}}?',
},
originallyFrom: {
name: 'ember-template-lint',
rule: 'lib/rules/no-potential-path-strings.js',
docs: 'docs/rule/no-potential-path-strings.md',
tests: 'test/unit/rules/no-potential-path-strings-test.js',
},
},

create(context) {
return {
GlimmerAttrNode(node) {
if (!node.value || node.value.type !== 'GlimmerTextNode') {
return;
}

const chars = node.value.chars;
const hasSpecialPrefix = chars.startsWith('this.') || chars.startsWith('@');

if (hasSpecialPrefix && !FINE_SYMBOLS.some((symbol) => chars.includes(symbol))) {
context.report({
node: node.value,
messageId: 'noPotentialPathStrings',
data: { path: chars },
});
}
},
};
},
};
154 changes: 154 additions & 0 deletions tests/lib/rules/template-no-potential-path-strings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
const { RuleTester } = require('eslint');
const rule = require('../../../lib/rules/template-no-potential-path-strings');

const ruleTester = new RuleTester({
parser: require.resolve('ember-eslint-parser'),
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
});

ruleTester.run('template-no-potential-path-strings', rule, {
valid: [
'<template><img src="foo.png"></template>',
'<template><img src={{picture}}></template>',
'<template><img src={{this.picture}}></template>',
'<template><img src={{@img}}></template>',
'<template><SomeComponent @foo={{@bar}} /></template>',
'<template><Ui::Demo @title="@my-org/my-package" /></template>',
'<template><Ui::Demo @title="@my-org\\my-package" /></template>',
'<template><Ui::Demo @title="@my-org|my-package" /></template>',
],

invalid: [
{
code: '<template><img src="this.picture"></template>',
output: null,
errors: [
{
message: 'Potential path in attribute string detected. Did you mean {{this.picture}}?',
},
],
},
{
code: '<template><img src=this.picture></template>',
output: null,
errors: [
{
message: 'Potential path in attribute string detected. Did you mean {{this.picture}}?',
},
],
},
{
code: '<template><img src="@img"></template>',
output: null,
errors: [
{
message: 'Potential path in attribute string detected. Did you mean {{@img}}?',
},
],
},
{
code: '<template><img src=@img></template>',
output: null,
errors: [
{
message: 'Potential path in attribute string detected. Did you mean {{@img}}?',
},
],
},
{
code: '<template><SomeComponent @foo=@bar /></template>',
output: null,
errors: [
{
message: 'Potential path in attribute string detected. Did you mean {{@bar}}?',
},
],
},
{
code: '<template><SomeComponent @foo=this.bar /></template>',
output: null,
errors: [
{
message: 'Potential path in attribute string detected. Did you mean {{this.bar}}?',
},
],
},
],
});

const hbsRuleTester = new RuleTester({
parser: require.resolve('ember-eslint-parser/hbs'),
parserOptions: {
ecmaVersion: 2022,
sourceType: 'module',
},
});

hbsRuleTester.run('template-no-potential-path-strings', rule, {
valid: [
'<img src="foo.png">',
'<img src={{picture}}>',
'<img src={{this.picture}}>',
'<img src={{@img}}>',
'<SomeComponent @foo={{@bar}} />',
'<Ui::Demo @title="@my-org/my-package" />',
'<Ui::Demo @title="@my-org\\my-package" />',
'<Ui::Demo @title="@my-org|my-package" />',
],
invalid: [
{
code: '<img src="this.picture">',
output: null,
errors: [
{
message: 'Potential path in attribute string detected. Did you mean {{this.picture}}?',
},
],
},
{
code: '<img src=this.picture>',
output: null,
errors: [
{
message: 'Potential path in attribute string detected. Did you mean {{this.picture}}?',
},
],
},
{
code: '<img src="@img">',
output: null,
errors: [
{
message: 'Potential path in attribute string detected. Did you mean {{@img}}?',
},
],
},
{
code: '<img src=@img>',
output: null,
errors: [
{
message: 'Potential path in attribute string detected. Did you mean {{@img}}?',
},
],
},
{
code: '<SomeComponent @foo=@bar />',
output: null,
errors: [
{
message: 'Potential path in attribute string detected. Did you mean {{@bar}}?',
},
],
},
{
code: '<SomeComponent @foo=this.bar />',
output: null,
errors: [
{
message: 'Potential path in attribute string detected. Did you mean {{this.bar}}?',
},
],
},
],
});
Loading