diff --git a/README.md b/README.md index 515d97f416..61cc01f8ae 100644 --- a/README.md +++ b/README.md @@ -247,6 +247,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-require-strict-mode](docs/rules/template-require-strict-mode.md) | require templates to be in strict mode | | | | | [template-require-valid-named-block-naming-format](docs/rules/template-require-valid-named-block-naming-format.md) | require valid named block naming format | | 🔧 | | | [template-self-closing-void-elements](docs/rules/template-self-closing-void-elements.md) | require self-closing on void elements | | 🔧 | | | [template-simple-modifiers](docs/rules/template-simple-modifiers.md) | require simple modifier syntax | | | | diff --git a/docs/rules/template-require-strict-mode.md b/docs/rules/template-require-strict-mode.md new file mode 100644 index 0000000000..cf76e9def5 --- /dev/null +++ b/docs/rules/template-require-strict-mode.md @@ -0,0 +1,57 @@ +# ember/template-require-strict-mode + +> **HBS Only**: This rule applies to classic `.hbs` template files only (loose mode). It is not relevant for `gjs`/`gts` files (strict mode), where these patterns cannot occur. + + + +Require templates to be in strict mode. + +Ember's Polaris edition component authoring format is template tag, which makes +templates follow "strict mode" semantics. + +This rule requires all templates to use strict mode (template tag). Effectively this +means you may only have template content in `.gjs`/`.gts` files, not in `.hbs` or +`.js`/`.ts`. + +## Examples + +This rule **forbids** the following: + +```hbs +// button.hbs + +``` + +```js +// button-test.js +import { hbs } from 'ember-cli-htmlbars'; + +test('it renders', async (assert) => { + await render(hbs``); + // ... +}); +``` + +This rule **allows** the following: + +```gjs +// button.gjs + +``` + +```gjs +// button-test.gjs +import { Button } from 'ember-awesome-button'; + +test('it renders', async (assert) => { + await render(); + // .. +}); +``` + +## References + +- [Template Tag Guide](https://guides.emberjs.com/release/components/template-tag-format/) +- [Strict Mode RFC](https://rfcs.emberjs.com/id/0496-handlebars-strict-mode/) diff --git a/lib/rules/template-require-strict-mode.js b/lib/rules/template-require-strict-mode.js new file mode 100644 index 0000000000..9e6bfccb06 --- /dev/null +++ b/lib/rules/template-require-strict-mode.js @@ -0,0 +1,44 @@ +const ERROR_MESSAGE = + 'Templates are required to be in strict mode. Consider refactoring to template tag format.'; + +function isStrictModeFile(filePath) { + return filePath?.endsWith('.gjs') || filePath?.endsWith('.gts'); +} + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: 'require templates to be in strict mode', + category: 'Best Practices', + recommended: false, + url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-require-strict-mode.md', + templateMode: 'loose', + }, + fixable: null, + schema: [], + messages: {}, + originallyFrom: { + name: 'ember-template-lint', + rule: 'lib/rules/require-strict-mode.js', + docs: 'docs/rule/require-strict-mode.md', + tests: 'test/unit/rules/require-strict-mode-test.js', + }, + }, + + create(context) { + const filePath = context.getFilename ? context.getFilename() : context.filename; + + return { + 'GlimmerTemplate:exit'(node) { + if (!isStrictModeFile(filePath)) { + context.report({ + node, + message: ERROR_MESSAGE, + }); + } + }, + }; + }, +}; diff --git a/tests/lib/rules/template-require-strict-mode.js b/tests/lib/rules/template-require-strict-mode.js new file mode 100644 index 0000000000..994b573060 --- /dev/null +++ b/tests/lib/rules/template-require-strict-mode.js @@ -0,0 +1,70 @@ +const rule = require('../../../lib/rules/template-require-strict-mode'); +const RuleTester = require('eslint').RuleTester; + +const ruleTester = new RuleTester({ + parser: require.resolve('ember-eslint-parser'), + parserOptions: { ecmaVersion: 2022, sourceType: 'module' }, +}); + +ruleTester.run('template-require-strict-mode', rule, { + valid: [ + { + filename: 'hello.gjs', + code: '', + }, + { + filename: 'hello.gts', + code: '', + }, + ], + invalid: [ + { + filename: 'hello.hbs', + code: '', + output: null, + errors: [ + { + message: + 'Templates are required to be in strict mode. Consider refactoring to template tag format.', + }, + ], + }, + { + filename: 'hello.hbs', + code: ``, + output: null, + errors: [ + { + message: + 'Templates are required to be in strict mode. Consider refactoring to template tag format.', + }, + ], + }, + ], +}); + +const hbsRuleTester = new RuleTester({ + parser: require.resolve('ember-eslint-parser/hbs'), + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module', + }, +}); + +hbsRuleTester.run('template-require-strict-mode', rule, { + valid: [], + invalid: [ + { + code: '
hello
', + output: null, + errors: [ + { + message: + 'Templates are required to be in strict mode. Consider refactoring to template tag format.', + }, + ], + }, + ], +});