Port 125 ember-template-lint rules for gjs/gts strict mode templates #3749
Annotations
48 errors
|
self-lint:
lib/rules/template-no-invalid-aria-attributes.js#L13
The meta properties should be placed in a consistent order: [type, docs, schema, messages]
|
|
self-lint:
lib/rules/template-no-html-comments.js#L29
The placeholder {{!-- --}} is missing (must provide it in the report's `data` object)
|
|
self-lint:
lib/rules/template-no-html-comments.js#L29
The placeholder {{!}} is missing (must provide it in the report's `data` object)
|
|
self-lint:
lib/rules/template-no-html-comments.js#L17
Insert `⏎·······`
|
|
self-lint:
lib/rules/template-no-html-comments.js#L15
The meta properties should be placed in a consistent order: [type, docs, schema, messages]
|
|
self-lint:
lib/rules/template-no-heading-inside-button.js#L20
Move function 'hasButtonParent' to the outer scope
|
|
self-lint:
lib/rules/template-no-empty-headings.js#L30
Replace `⏎··········child.type·===·'GlimmerMustacheStatement'·||⏎··········child.type·===·'GlimmerBlockStatement'⏎········` with `child.type·===·'GlimmerMustacheStatement'·||·child.type·===·'GlimmerBlockStatement'`
|
|
self-lint:
lib/rules/template-no-empty-headings.js#L22
Expected { after 'if' condition
|
|
self-lint:
lib/rules/template-no-empty-headings.js#L21
Move function 'hasTextContent' to the outer scope
|
|
self-lint:
lib/rules/template-no-bare-yield.js#L13
The meta properties should be placed in a consistent order: [type, docs, schema, messages]
|
|
tests/rule-setup.js > rules setup is correct > should mention all rules in the README:
tests/rule-setup.js#L81
AssertionError: expected '# eslint-plugin-ember\n\n[](https://npmjs.org/package/eslint-plugin-ember)
+ [](https://npmjs.org/package/eslint-plugin-ember)
+ 
+
+ > An ESLint plugin that provides a set of rules for Ember applications based on commonly known good practices.
+
+ ## ❗️Requirements
+
+ - [ESLint](https://eslint.org/) `>= 8`
+ - [Node.js](https://nodejs.org/) `18.* || 20.* || >= 21`
+
+ ## 🚀 Usage
+
+ ### 1. Install plugin
+
+ ```shell
+ npm install --save-dev eslint-plugin-ember
+ ```
+
+ ### 2. Update your config
+
+ ```js
+ // eslint.config.js (flat config)
+ const eslintPluginEmberRecommended = require('eslint-plugin-ember/configs/recommended');
+
+ module.exports = [
+ ...eslintPluginEmberRecommended,
+ ];
+ ```
+
+ or
+
+ ```js
+ // .eslintrc.js (legacy config)
+ module.exports = {
+ plugins: ['ember'],
+ extends: [
+ 'eslint:recommended',
+ 'plugin:ember/recommended' // or other configuration
+ ],
+ rules: {
+ // override / enable optional rules
+ 'ember/no-replace-test-comments': 'error'
+ }
+ };
+ ```
+
+ ## gts/gjs
+
+ lint files having `First-Class Component Templates` (fcct)
+
+ learn more [here](https://github.com/ember-template-imports/ember-template-imports)
+
+ > [!NOTE]
+ > special care should be used when setting up parsers, since they cannot be overwritten. thus they should be used in override only and specific to file types
+
+ gjs/gts support is provided by the [ember-eslint-parser](https://github.com/NullVoxPopuli/ember-eslint-parser)
+
+ > [!NOTE]
+ > if you import .gts files in .ts files, then `ember-eslint-parser` is required for .ts as well to enable typed linting
+
+ ```js
+ // .eslintrc.js
+ module.exports = {
+ overrides: [
+ {
+ files: ['**/*.{js,ts}'],
+ plugins: ['ember'],
+ parser: '@typescript-eslint/parser',
+ extends: [
+ 'eslint:recommended',
+ 'plugin:ember/recommended', // or other configuration
+ ],
+ rules: {
+ // override / enable optional rules
+ 'ember/no-replace-test-comments': 'error'
+ }
+ },
+ {
+ files: ['**/*.gts'],
+ parser: 'ember-eslint-parser',
+ plugins: ['ember'],
+ extends: [
+ 'eslint:recommended',
+ 'plugin:@typescript-eslint/recommended',
+ 'plugin:ember/recommended',
+ 'plugin:ember/recommended-gts',
+ ],
+ },
+ {
+ files: ['**/*.gjs'],
+ parser: 'ember-eslint-parser',
+ plugins: ['ember'],
+ extends: [
+ 'eslint:recommended',
+ 'plugin:ember/recommended',
+ 'plugin:ember/recommended-gjs',
+ ],
+ },
+ {
+ files: ['tests/**/*.{js,ts,gjs,gts}'],
+ rules: {
+ // override / enable optional rules
+ 'ember/no-replace-test-comments': 'error'
+ }
+ },
+ ],
+ };
+ ```
+
+ ### Strict Template Linting (Optional)
+
+ For enhanced template linting in `gjs`/`gts` files, use the `strict-gjs` or `strict-gts` configs. These include additional template rules ported from `ember-template-lint`:
+
+ ```javascript
+ // eslint.config.js
+ const ember = require('eslint-plugin-ember');
+
+ module.exports = [
+ ...ember.configs.recommended,
+ {
+ files: ['**/*.gts'],
+ extends: ['plugin:ember/strict-gts'],
+ },
+ {
+ files: ['**/*.gjs'],
+ extends: ['plugin:ember/strict-gjs'],
+ },
+ ];
+ ```
+
+
+ ### rules applied to fcct templates
+
+ - semi rule, same as [prettier plugin](https://github.com/gitKrystan/prettier-plugin-ember-template-tag/issues/1)
+ - no-undef rule will take effect for template vars (includes js scope)
+ - no-unused rule will take effect for template block
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-simple-unless > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'require simple conditions in unless blocks',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-simple-unless.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ simpleUnless:
+ 'unless blocks should use simple conditions. Use if with negated condition for complex logic.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerBlockStatement(node) {
+ if (node.path.type === 'GlimmerPathExpression' && node.path.original === 'unless') {
+ if (node.params.length > 0) {
+ const firstParam = node.params[0];
+ if (firstParam.type === 'GlimmerSubExpression') {
+ context.report({
+ node: firstParam,
+ messageId: 'simpleUnless',
+ });
+ }
+ }
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-require-each-key > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'require key attribute in {{#each}} loops',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-require-each-key.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ requireEachKey: 'each block should have a key attribute for better rendering performance.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerBlockStatement(node) {
+ if (node.path.type === 'GlimmerPathExpression' && node.path.original === 'each') {
+ const hasKey = node.hash && node.hash.pairs.some((pair) => pair.key === 'key');
+ if (!hasKey) {
+ context.report({
+ node,
+ messageId: 'requireEachKey',
+ });
+ }
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-unnecessary-curly-in-string-attrs > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'disallow unnecessary curly braces in attributes',
+ category: 'Style',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-unnecessary-curly-in-string-attrs.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ unnecessaryCurlyInStringAttr:
+ 'Unnecessary curly braces around string attribute. Use static string instead.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerAttrNode(node) {
+ if (
+ node.value.type === 'GlimmerMustacheStatement' &&
+ node.value.path.type === 'GlimmerStringLiteral'
+ ) {
+ context.report({
+ node: node.value,
+ messageId: 'unnecessaryCurlyInStringAttr',
+ });
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-outlet-outside-routes > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'disallow {{outlet}} outside of route templates',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-outlet-outside-routes.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ noOutletOutsideRoutes: 'outlet should only be used in route templates.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerMustacheStatement(node) {
+ if (
+ node.path.type === 'GlimmerPathExpression' &&
+ node.path.original === 'outlet'
+ ) {
+ context.report({
+ node,
+ messageId: 'noOutletOutsideRoutes',
+ });
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-negated-condition > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'disallow negated conditions in if/unless',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-negated-condition.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ noNegatedCondition:
+ 'Unexpected negated condition. Use unless helper or rewrite condition.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerBlockStatement(node) {
+ if (node.path.type === 'GlimmerPathExpression' && node.path.original === 'if') {
+ const firstParam = node.params[0];
+ if (
+ firstParam &&
+ firstParam.type === 'GlimmerSubExpression' &&
+ firstParam.path.type === 'GlimmerPathExpression' &&
+ firstParam.path.original === 'not'
+ ) {
+ context.report({
+ node: firstParam,
+ messageId: 'noNegatedCondition',
+ });
+ }
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-invalid-link-title > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'disallow invalid title attributes on link elements',
+ category: 'Accessibility',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-invalid-link-title.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ noInvalidLinkTitle:
+ 'Link title attribute should not be the same as link text or empty.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerElementNode(node) {
+ if (node.tag !== 'a') {
+ return;
+ }
+
+ const titleAttr = node.attributes.find(
+ (attr) => attr.type === 'GlimmerAttrNode' && attr.name === 'title'
+ );
+
+ if (!titleAttr || titleAttr.value.type !== 'GlimmerTextNode') {
+ return;
+ }
+
+ const titleValue = titleAttr.value.chars.trim();
+
+ if (!titleValue) {
+ context.report({
+ node: titleAttr,
+ messageId: 'noInvalidLinkTitle',
+ });
+ return;
+ }
+
+ if (node.children.length === 1 && node.children[0].type === 'GlimmerTextNode') {
+ const textContent = node.children[0].chars.trim();
+ if (textContent && textContent === titleValue) {
+ context.report({
+ node: titleAttr,
+ messageId: 'noInvalidLinkTitle',
+ });
+ }
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-invalid-aria-attributes > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'disallow invalid aria-* attributes',
+ category: 'Accessibility',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-invalid-aria-attributes.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ noInvalidAriaAttribute: 'Invalid ARIA attribute: {{attribute}}',
+ },
+ },
+
+ create(context) {
+ const validAriaAttributes = [
+ 'aria-activedescendant',
+ 'aria-atomic',
+ 'aria-autocomplete',
+ 'aria-busy',
+ 'aria-checked',
+ 'aria-colcount',
+ 'aria-colindex',
+ 'aria-colspan',
+ 'aria-controls',
+ 'aria-current',
+ 'aria-describedby',
+ 'aria-details',
+ 'aria-disabled',
+ 'aria-dropeffect',
+ 'aria-errormessage',
+ 'aria-expanded',
+ 'aria-flowto',
+ 'aria-grabbed',
+ 'aria-haspopup',
+ 'aria-hidden',
+ 'aria-invalid',
+ 'aria-keyshortcuts',
+ 'aria-label',
+ 'aria-labelledby',
+ 'aria-level',
+ 'aria-live',
+ 'aria-modal',
+ 'aria-multiline',
+ 'aria-multiselectable',
+ 'aria-orientation',
+ 'aria-owns',
+ 'aria-placeholder',
+ 'aria-posinset',
+ 'aria-pressed',
+ 'aria-readonly',
+ 'aria-relevant',
+ 'aria-required',
+ 'aria-roledescription',
+ 'aria-rowcount',
+ 'aria-rowindex',
+ 'aria-rowspan',
+ 'aria-selected',
+ 'aria-setsize',
+ 'aria-sort',
+ 'aria-valuemax',
+ 'aria-valuemin',
+ 'aria-valuenow',
+ 'aria-valuetext',
+ ];
+
+ return {
+ GlimmerAttrNode(node) {
+ if (node.name.startsWith('aria-')) {
+ if (!validAriaAttributes.includes(node.name)) {
+ context.report({
+ node,
+ messageId: 'noInvalidAriaAttribute',
+ data: { attribute: node.name },
+ });
+ }
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-html-comments > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nconst { getSourceC…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ const { getSourceCode } = require('../utils/utils');
+
+ module.exports = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'disallow HTML comments in templates',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-html-comments.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ noHtmlComments: 'HTML comments should not be used in templates. Use {{! }} or {{!-- --}} instead.',
+ },
+ },
+
+ create(context) {
+ const sourceCode = getSourceCode(context);
+
+ return {
+ GlimmerCommentStatement(node) {
+ if (node.value && node.value.startsWith('<!--')) {
+ context.report({
+ node,
+ messageId: 'noHtmlComments',
+ });
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-bare-yield > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'disallow {{yield}} without parameters outside of contextual components',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-bare-yield.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ noBareYield: 'yield should have parameters or be used in contextual components only.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerMustacheStatement(node) {
+ if (
+ node.path.type === 'GlimmerPathExpression' &&
+ node.path.original === 'yield' &&
+ (!node.params || node.params.length === 0)
+ ) {
+ context.report({
+ node,
+ messageId: 'noBareYield',
+ });
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
build (ubuntu, 20.x)
The strategy configuration was canceled because "build.ubuntu_22_x" failed
|
|
tests/rule-setup.js > rules setup is correct > should mention all rules in the README:
tests/rule-setup.js#L81
AssertionError: expected '# eslint-plugin-ember\n\n[](https://npmjs.org/package/eslint-plugin-ember)
+ [](https://npmjs.org/package/eslint-plugin-ember)
+ 
+
+ > An ESLint plugin that provides a set of rules for Ember applications based on commonly known good practices.
+
+ ## ❗️Requirements
+
+ - [ESLint](https://eslint.org/) `>= 8`
+ - [Node.js](https://nodejs.org/) `18.* || 20.* || >= 21`
+
+ ## 🚀 Usage
+
+ ### 1. Install plugin
+
+ ```shell
+ npm install --save-dev eslint-plugin-ember
+ ```
+
+ ### 2. Update your config
+
+ ```js
+ // eslint.config.js (flat config)
+ const eslintPluginEmberRecommended = require('eslint-plugin-ember/configs/recommended');
+
+ module.exports = [
+ ...eslintPluginEmberRecommended,
+ ];
+ ```
+
+ or
+
+ ```js
+ // .eslintrc.js (legacy config)
+ module.exports = {
+ plugins: ['ember'],
+ extends: [
+ 'eslint:recommended',
+ 'plugin:ember/recommended' // or other configuration
+ ],
+ rules: {
+ // override / enable optional rules
+ 'ember/no-replace-test-comments': 'error'
+ }
+ };
+ ```
+
+ ## gts/gjs
+
+ lint files having `First-Class Component Templates` (fcct)
+
+ learn more [here](https://github.com/ember-template-imports/ember-template-imports)
+
+ > [!NOTE]
+ > special care should be used when setting up parsers, since they cannot be overwritten. thus they should be used in override only and specific to file types
+
+ gjs/gts support is provided by the [ember-eslint-parser](https://github.com/NullVoxPopuli/ember-eslint-parser)
+
+ > [!NOTE]
+ > if you import .gts files in .ts files, then `ember-eslint-parser` is required for .ts as well to enable typed linting
+
+ ```js
+ // .eslintrc.js
+ module.exports = {
+ overrides: [
+ {
+ files: ['**/*.{js,ts}'],
+ plugins: ['ember'],
+ parser: '@typescript-eslint/parser',
+ extends: [
+ 'eslint:recommended',
+ 'plugin:ember/recommended', // or other configuration
+ ],
+ rules: {
+ // override / enable optional rules
+ 'ember/no-replace-test-comments': 'error'
+ }
+ },
+ {
+ files: ['**/*.gts'],
+ parser: 'ember-eslint-parser',
+ plugins: ['ember'],
+ extends: [
+ 'eslint:recommended',
+ 'plugin:@typescript-eslint/recommended',
+ 'plugin:ember/recommended',
+ 'plugin:ember/recommended-gts',
+ ],
+ },
+ {
+ files: ['**/*.gjs'],
+ parser: 'ember-eslint-parser',
+ plugins: ['ember'],
+ extends: [
+ 'eslint:recommended',
+ 'plugin:ember/recommended',
+ 'plugin:ember/recommended-gjs',
+ ],
+ },
+ {
+ files: ['tests/**/*.{js,ts,gjs,gts}'],
+ rules: {
+ // override / enable optional rules
+ 'ember/no-replace-test-comments': 'error'
+ }
+ },
+ ],
+ };
+ ```
+
+ ### Strict Template Linting (Optional)
+
+ For enhanced template linting in `gjs`/`gts` files, use the `strict-gjs` or `strict-gts` configs. These include additional template rules ported from `ember-template-lint`:
+
+ ```javascript
+ // eslint.config.js
+ const ember = require('eslint-plugin-ember');
+
+ module.exports = [
+ ...ember.configs.recommended,
+ {
+ files: ['**/*.gts'],
+ extends: ['plugin:ember/strict-gts'],
+ },
+ {
+ files: ['**/*.gjs'],
+ extends: ['plugin:ember/strict-gjs'],
+ },
+ ];
+ ```
+
+
+ ### rules applied to fcct templates
+
+ - semi rule, same as [prettier plugin](https://github.com/gitKrystan/prettier-plugin-ember-template-tag/issues/1)
+ - no-undef rule will take effect for template vars (includes js scope)
+ - no-unused rule will take effect for template block
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-simple-unless > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'require simple conditions in unless blocks',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-simple-unless.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ simpleUnless:
+ 'unless blocks should use simple conditions. Use if with negated condition for complex logic.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerBlockStatement(node) {
+ if (node.path.type === 'GlimmerPathExpression' && node.path.original === 'unless') {
+ if (node.params.length > 0) {
+ const firstParam = node.params[0];
+ if (firstParam.type === 'GlimmerSubExpression') {
+ context.report({
+ node: firstParam,
+ messageId: 'simpleUnless',
+ });
+ }
+ }
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-require-each-key > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'require key attribute in {{#each}} loops',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-require-each-key.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ requireEachKey: 'each block should have a key attribute for better rendering performance.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerBlockStatement(node) {
+ if (node.path.type === 'GlimmerPathExpression' && node.path.original === 'each') {
+ const hasKey = node.hash && node.hash.pairs.some((pair) => pair.key === 'key');
+ if (!hasKey) {
+ context.report({
+ node,
+ messageId: 'requireEachKey',
+ });
+ }
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-unnecessary-curly-in-string-attrs > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'disallow unnecessary curly braces in attributes',
+ category: 'Style',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-unnecessary-curly-in-string-attrs.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ unnecessaryCurlyInStringAttr:
+ 'Unnecessary curly braces around string attribute. Use static string instead.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerAttrNode(node) {
+ if (
+ node.value.type === 'GlimmerMustacheStatement' &&
+ node.value.path.type === 'GlimmerStringLiteral'
+ ) {
+ context.report({
+ node: node.value,
+ messageId: 'unnecessaryCurlyInStringAttr',
+ });
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-outlet-outside-routes > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'disallow {{outlet}} outside of route templates',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-outlet-outside-routes.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ noOutletOutsideRoutes: 'outlet should only be used in route templates.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerMustacheStatement(node) {
+ if (
+ node.path.type === 'GlimmerPathExpression' &&
+ node.path.original === 'outlet'
+ ) {
+ context.report({
+ node,
+ messageId: 'noOutletOutsideRoutes',
+ });
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-negated-condition > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'disallow negated conditions in if/unless',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-negated-condition.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ noNegatedCondition:
+ 'Unexpected negated condition. Use unless helper or rewrite condition.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerBlockStatement(node) {
+ if (node.path.type === 'GlimmerPathExpression' && node.path.original === 'if') {
+ const firstParam = node.params[0];
+ if (
+ firstParam &&
+ firstParam.type === 'GlimmerSubExpression' &&
+ firstParam.path.type === 'GlimmerPathExpression' &&
+ firstParam.path.original === 'not'
+ ) {
+ context.report({
+ node: firstParam,
+ messageId: 'noNegatedCondition',
+ });
+ }
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-invalid-link-title > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'disallow invalid title attributes on link elements',
+ category: 'Accessibility',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-invalid-link-title.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ noInvalidLinkTitle:
+ 'Link title attribute should not be the same as link text or empty.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerElementNode(node) {
+ if (node.tag !== 'a') {
+ return;
+ }
+
+ const titleAttr = node.attributes.find(
+ (attr) => attr.type === 'GlimmerAttrNode' && attr.name === 'title'
+ );
+
+ if (!titleAttr || titleAttr.value.type !== 'GlimmerTextNode') {
+ return;
+ }
+
+ const titleValue = titleAttr.value.chars.trim();
+
+ if (!titleValue) {
+ context.report({
+ node: titleAttr,
+ messageId: 'noInvalidLinkTitle',
+ });
+ return;
+ }
+
+ if (node.children.length === 1 && node.children[0].type === 'GlimmerTextNode') {
+ const textContent = node.children[0].chars.trim();
+ if (textContent && textContent === titleValue) {
+ context.report({
+ node: titleAttr,
+ messageId: 'noInvalidLinkTitle',
+ });
+ }
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-invalid-aria-attributes > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'disallow invalid aria-* attributes',
+ category: 'Accessibility',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-invalid-aria-attributes.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ noInvalidAriaAttribute: 'Invalid ARIA attribute: {{attribute}}',
+ },
+ },
+
+ create(context) {
+ const validAriaAttributes = [
+ 'aria-activedescendant',
+ 'aria-atomic',
+ 'aria-autocomplete',
+ 'aria-busy',
+ 'aria-checked',
+ 'aria-colcount',
+ 'aria-colindex',
+ 'aria-colspan',
+ 'aria-controls',
+ 'aria-current',
+ 'aria-describedby',
+ 'aria-details',
+ 'aria-disabled',
+ 'aria-dropeffect',
+ 'aria-errormessage',
+ 'aria-expanded',
+ 'aria-flowto',
+ 'aria-grabbed',
+ 'aria-haspopup',
+ 'aria-hidden',
+ 'aria-invalid',
+ 'aria-keyshortcuts',
+ 'aria-label',
+ 'aria-labelledby',
+ 'aria-level',
+ 'aria-live',
+ 'aria-modal',
+ 'aria-multiline',
+ 'aria-multiselectable',
+ 'aria-orientation',
+ 'aria-owns',
+ 'aria-placeholder',
+ 'aria-posinset',
+ 'aria-pressed',
+ 'aria-readonly',
+ 'aria-relevant',
+ 'aria-required',
+ 'aria-roledescription',
+ 'aria-rowcount',
+ 'aria-rowindex',
+ 'aria-rowspan',
+ 'aria-selected',
+ 'aria-setsize',
+ 'aria-sort',
+ 'aria-valuemax',
+ 'aria-valuemin',
+ 'aria-valuenow',
+ 'aria-valuetext',
+ ];
+
+ return {
+ GlimmerAttrNode(node) {
+ if (node.name.startsWith('aria-')) {
+ if (!validAriaAttributes.includes(node.name)) {
+ context.report({
+ node,
+ messageId: 'noInvalidAriaAttribute',
+ data: { attribute: node.name },
+ });
+ }
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-html-comments > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nconst { getSourceC…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ const { getSourceCode } = require('../utils/utils');
+
+ module.exports = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'disallow HTML comments in templates',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-html-comments.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ noHtmlComments: 'HTML comments should not be used in templates. Use {{! }} or {{!-- --}} instead.',
+ },
+ },
+
+ create(context) {
+ const sourceCode = getSourceCode(context);
+
+ return {
+ GlimmerCommentStatement(node) {
+ if (node.value && node.value.startsWith('<!--')) {
+ context.report({
+ node,
+ messageId: 'noHtmlComments',
+ });
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-bare-yield > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'disallow {{yield}} without parameters outside of contextual components',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-bare-yield.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ noBareYield: 'yield should have parameters or be used in contextual components only.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerMustacheStatement(node) {
+ if (
+ node.path.type === 'GlimmerPathExpression' &&
+ node.path.original === 'yield' &&
+ (!node.params || node.params.length === 0)
+ ) {
+ context.report({
+ node,
+ messageId: 'noBareYield',
+ });
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
build (ubuntu, 18.x)
The strategy configuration was canceled because "build.ubuntu_22_x" failed
|
|
tests/rule-setup.js > rules setup is correct > should mention all rules in the README:
tests/rule-setup.js#L81
AssertionError: expected '# eslint-plugin-ember\n\n[](https://npmjs.org/package/eslint-plugin-ember)
+ [](https://npmjs.org/package/eslint-plugin-ember)
+ 
+
+ > An ESLint plugin that provides a set of rules for Ember applications based on commonly known good practices.
+
+ ## ❗️Requirements
+
+ - [ESLint](https://eslint.org/) `>= 8`
+ - [Node.js](https://nodejs.org/) `18.* || 20.* || >= 21`
+
+ ## 🚀 Usage
+
+ ### 1. Install plugin
+
+ ```shell
+ npm install --save-dev eslint-plugin-ember
+ ```
+
+ ### 2. Update your config
+
+ ```js
+ // eslint.config.js (flat config)
+ const eslintPluginEmberRecommended = require('eslint-plugin-ember/configs/recommended');
+
+ module.exports = [
+ ...eslintPluginEmberRecommended,
+ ];
+ ```
+
+ or
+
+ ```js
+ // .eslintrc.js (legacy config)
+ module.exports = {
+ plugins: ['ember'],
+ extends: [
+ 'eslint:recommended',
+ 'plugin:ember/recommended' // or other configuration
+ ],
+ rules: {
+ // override / enable optional rules
+ 'ember/no-replace-test-comments': 'error'
+ }
+ };
+ ```
+
+ ## gts/gjs
+
+ lint files having `First-Class Component Templates` (fcct)
+
+ learn more [here](https://github.com/ember-template-imports/ember-template-imports)
+
+ > [!NOTE]
+ > special care should be used when setting up parsers, since they cannot be overwritten. thus they should be used in override only and specific to file types
+
+ gjs/gts support is provided by the [ember-eslint-parser](https://github.com/NullVoxPopuli/ember-eslint-parser)
+
+ > [!NOTE]
+ > if you import .gts files in .ts files, then `ember-eslint-parser` is required for .ts as well to enable typed linting
+
+ ```js
+ // .eslintrc.js
+ module.exports = {
+ overrides: [
+ {
+ files: ['**/*.{js,ts}'],
+ plugins: ['ember'],
+ parser: '@typescript-eslint/parser',
+ extends: [
+ 'eslint:recommended',
+ 'plugin:ember/recommended', // or other configuration
+ ],
+ rules: {
+ // override / enable optional rules
+ 'ember/no-replace-test-comments': 'error'
+ }
+ },
+ {
+ files: ['**/*.gts'],
+ parser: 'ember-eslint-parser',
+ plugins: ['ember'],
+ extends: [
+ 'eslint:recommended',
+ 'plugin:@typescript-eslint/recommended',
+ 'plugin:ember/recommended',
+ 'plugin:ember/recommended-gts',
+ ],
+ },
+ {
+ files: ['**/*.gjs'],
+ parser: 'ember-eslint-parser',
+ plugins: ['ember'],
+ extends: [
+ 'eslint:recommended',
+ 'plugin:ember/recommended',
+ 'plugin:ember/recommended-gjs',
+ ],
+ },
+ {
+ files: ['tests/**/*.{js,ts,gjs,gts}'],
+ rules: {
+ // override / enable optional rules
+ 'ember/no-replace-test-comments': 'error'
+ }
+ },
+ ],
+ };
+ ```
+
+ ### Strict Template Linting (Optional)
+
+ For enhanced template linting in `gjs`/`gts` files, use the `strict-gjs` or `strict-gts` configs. These include additional template rules ported from `ember-template-lint`:
+
+ ```javascript
+ // eslint.config.js
+ const ember = require('eslint-plugin-ember');
+
+ module.exports = [
+ ...ember.configs.recommended,
+ {
+ files: ['**/*.gts'],
+ extends: ['plugin:ember/strict-gts'],
+ },
+ {
+ files: ['**/*.gjs'],
+ extends: ['plugin:ember/strict-gjs'],
+ },
+ ];
+ ```
+
+
+ ### rules applied to fcct templates
+
+ - semi rule, same as [prettier plugin](https://github.com/gitKrystan/prettier-plugin-ember-template-tag/issues/1)
+ - no-undef rule will take effect for template vars (includes js scope)
+ - no-unused rule will take effect for template block
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-simple-unless > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'require simple conditions in unless blocks',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-simple-unless.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ simpleUnless:
+ 'unless blocks should use simple conditions. Use if with negated condition for complex logic.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerBlockStatement(node) {
+ if (node.path.type === 'GlimmerPathExpression' && node.path.original === 'unless') {
+ if (node.params.length > 0) {
+ const firstParam = node.params[0];
+ if (firstParam.type === 'GlimmerSubExpression') {
+ context.report({
+ node: firstParam,
+ messageId: 'simpleUnless',
+ });
+ }
+ }
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-require-each-key > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'require key attribute in {{#each}} loops',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-require-each-key.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ requireEachKey: 'each block should have a key attribute for better rendering performance.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerBlockStatement(node) {
+ if (node.path.type === 'GlimmerPathExpression' && node.path.original === 'each') {
+ const hasKey = node.hash && node.hash.pairs.some((pair) => pair.key === 'key');
+ if (!hasKey) {
+ context.report({
+ node,
+ messageId: 'requireEachKey',
+ });
+ }
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-unnecessary-curly-in-string-attrs > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'disallow unnecessary curly braces in attributes',
+ category: 'Style',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-unnecessary-curly-in-string-attrs.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ unnecessaryCurlyInStringAttr:
+ 'Unnecessary curly braces around string attribute. Use static string instead.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerAttrNode(node) {
+ if (
+ node.value.type === 'GlimmerMustacheStatement' &&
+ node.value.path.type === 'GlimmerStringLiteral'
+ ) {
+ context.report({
+ node: node.value,
+ messageId: 'unnecessaryCurlyInStringAttr',
+ });
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-outlet-outside-routes > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'disallow {{outlet}} outside of route templates',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-outlet-outside-routes.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ noOutletOutsideRoutes: 'outlet should only be used in route templates.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerMustacheStatement(node) {
+ if (
+ node.path.type === 'GlimmerPathExpression' &&
+ node.path.original === 'outlet'
+ ) {
+ context.report({
+ node,
+ messageId: 'noOutletOutsideRoutes',
+ });
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-negated-condition > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'disallow negated conditions in if/unless',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-negated-condition.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ noNegatedCondition:
+ 'Unexpected negated condition. Use unless helper or rewrite condition.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerBlockStatement(node) {
+ if (node.path.type === 'GlimmerPathExpression' && node.path.original === 'if') {
+ const firstParam = node.params[0];
+ if (
+ firstParam &&
+ firstParam.type === 'GlimmerSubExpression' &&
+ firstParam.path.type === 'GlimmerPathExpression' &&
+ firstParam.path.original === 'not'
+ ) {
+ context.report({
+ node: firstParam,
+ messageId: 'noNegatedCondition',
+ });
+ }
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-invalid-link-title > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'disallow invalid title attributes on link elements',
+ category: 'Accessibility',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-invalid-link-title.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ noInvalidLinkTitle:
+ 'Link title attribute should not be the same as link text or empty.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerElementNode(node) {
+ if (node.tag !== 'a') {
+ return;
+ }
+
+ const titleAttr = node.attributes.find(
+ (attr) => attr.type === 'GlimmerAttrNode' && attr.name === 'title'
+ );
+
+ if (!titleAttr || titleAttr.value.type !== 'GlimmerTextNode') {
+ return;
+ }
+
+ const titleValue = titleAttr.value.chars.trim();
+
+ if (!titleValue) {
+ context.report({
+ node: titleAttr,
+ messageId: 'noInvalidLinkTitle',
+ });
+ return;
+ }
+
+ if (node.children.length === 1 && node.children[0].type === 'GlimmerTextNode') {
+ const textContent = node.children[0].chars.trim();
+ if (textContent && textContent === titleValue) {
+ context.report({
+ node: titleAttr,
+ messageId: 'noInvalidLinkTitle',
+ });
+ }
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-invalid-aria-attributes > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'disallow invalid aria-* attributes',
+ category: 'Accessibility',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-invalid-aria-attributes.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ noInvalidAriaAttribute: 'Invalid ARIA attribute: {{attribute}}',
+ },
+ },
+
+ create(context) {
+ const validAriaAttributes = [
+ 'aria-activedescendant',
+ 'aria-atomic',
+ 'aria-autocomplete',
+ 'aria-busy',
+ 'aria-checked',
+ 'aria-colcount',
+ 'aria-colindex',
+ 'aria-colspan',
+ 'aria-controls',
+ 'aria-current',
+ 'aria-describedby',
+ 'aria-details',
+ 'aria-disabled',
+ 'aria-dropeffect',
+ 'aria-errormessage',
+ 'aria-expanded',
+ 'aria-flowto',
+ 'aria-grabbed',
+ 'aria-haspopup',
+ 'aria-hidden',
+ 'aria-invalid',
+ 'aria-keyshortcuts',
+ 'aria-label',
+ 'aria-labelledby',
+ 'aria-level',
+ 'aria-live',
+ 'aria-modal',
+ 'aria-multiline',
+ 'aria-multiselectable',
+ 'aria-orientation',
+ 'aria-owns',
+ 'aria-placeholder',
+ 'aria-posinset',
+ 'aria-pressed',
+ 'aria-readonly',
+ 'aria-relevant',
+ 'aria-required',
+ 'aria-roledescription',
+ 'aria-rowcount',
+ 'aria-rowindex',
+ 'aria-rowspan',
+ 'aria-selected',
+ 'aria-setsize',
+ 'aria-sort',
+ 'aria-valuemax',
+ 'aria-valuemin',
+ 'aria-valuenow',
+ 'aria-valuetext',
+ ];
+
+ return {
+ GlimmerAttrNode(node) {
+ if (node.name.startsWith('aria-')) {
+ if (!validAriaAttributes.includes(node.name)) {
+ context.report({
+ node,
+ messageId: 'noInvalidAriaAttribute',
+ data: { attribute: node.name },
+ });
+ }
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-html-comments > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nconst { getSourceC…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ const { getSourceCode } = require('../utils/utils');
+
+ module.exports = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'disallow HTML comments in templates',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-html-comments.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ noHtmlComments: 'HTML comments should not be used in templates. Use {{! }} or {{!-- --}} instead.',
+ },
+ },
+
+ create(context) {
+ const sourceCode = getSourceCode(context);
+
+ return {
+ GlimmerCommentStatement(node) {
+ if (node.value && node.value.startsWith('<!--')) {
+ context.report({
+ node,
+ messageId: 'noHtmlComments',
+ });
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
tests/rule-setup.js > rules setup is correct > rule files > template-no-bare-yield > should have the jsdoc comment for rule type:
tests/rule-setup.js#L46
AssertionError: expected '\'use strict\';\n\nmodule.exports = {…' to contain '/** @type {import(\'eslint\').Rule.Ru…'
- Expected
+ Received
- /** @type {import('eslint').Rule.RuleModule} */
+ 'use strict';
+
+ module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'disallow {{yield}} without parameters outside of contextual components',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-bare-yield.md',
+ },
+ strictGjs: true,
+ strictGts: true,
+ schema: [],
+ messages: {
+ noBareYield: 'yield should have parameters or be used in contextual components only.',
+ },
+ },
+
+ create(context) {
+ return {
+ GlimmerMustacheStatement(node) {
+ if (
+ node.path.type === 'GlimmerPathExpression' &&
+ node.path.original === 'yield' &&
+ (!node.params || node.params.length === 0)
+ ) {
+ context.report({
+ node,
+ messageId: 'noBareYield',
+ });
+ }
+ },
+ };
+ },
+ };
+
❯ tests/rule-setup.js:46:24
|
|
build (windows, 18.x)
The strategy configuration was canceled because "build.ubuntu_22_x" failed
|
|
build (windows, 18.x)
The operation was canceled.
|
|
build (windows, 20.x)
The strategy configuration was canceled because "build.ubuntu_22_x" failed
|
|
build (windows, 20.x)
The operation was canceled.
|
|
build (windows, 22.x)
The strategy configuration was canceled because "build.ubuntu_22_x" failed
|
|
build (windows, 22.x)
The operation was canceled.
|