Skip to content

introduce eslint-plugin-boxel package, with template-missing-invokable rule #2453

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 22, 2025
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
4 changes: 4 additions & 0 deletions .github/workflows/ci-lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ jobs:
if: always()
run: pnpm run lint
working-directory: packages/vscode-boxel-tools
- name: Lint ESLint Plugin
if: always()
run: pnpm run lint
working-directory: packages/eslint-plugin-boxel
- name: Lint AI Bot
if: always()
run: pnpm run lint
Expand Down
18 changes: 18 additions & 0 deletions eslint/missing-invokables-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* eslint-disable no-undef */
'use strict';

module.exports = {
invokables: {
fn: ['fn', '@ember/helper'],
on: ['on', '@ember/modifier'],
and: ['and', '@cardstack/boxel-ui/helpers'],
bool: ['bool', '@cardstack/boxel-ui/helpers'],
eq: ['eq', '@cardstack/boxel-ui/helpers'],
gt: ['gt', '@cardstack/boxel-ui/helpers'],
lt: ['lt', '@cardstack/boxel-ui/helpers'],
not: ['not', '@cardstack/boxel-ui/helpers'],
or: ['or', '@cardstack/boxel-ui/helpers'],
add: ['add', '@cardstack/boxel-ui/helpers'],
subtract: ['subtract', '@cardstack/boxel-ui/helpers'],
},
};
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@
"@glint/environment-ember-loose": "1.3.0",
"@glint/environment-ember-template-imports": "1.3.0",
"@playwright/test": "^1.48.0",
"@typescript-eslint/eslint-plugin": "^5.17.0",
"@typescript-eslint/parser": "^5.17.0",
"@typescript-eslint/eslint-plugin": "^7.16.1",
"@typescript-eslint/parser": "^7.16.1",
"ember-cli-htmlbars": "^6.3.0",
"ember-resources": "^6.5.1",
"ember-source": "~5.4.0",
Expand Down
36 changes: 36 additions & 0 deletions packages/boxel-icons/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

const MISSING_INVOKABLES_CONFIG = require('../../eslint/missing-invokables-config');

module.exports = {
root: true,
parser: '@typescript-eslint/parser',
Expand Down Expand Up @@ -35,6 +37,40 @@ module.exports = {
],
},
overrides: [
{
files: ['**/*.gts'],
parser: 'ember-eslint-parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
requireConfigFile: false,
babelOptions: {
plugins: [
[
'@babel/plugin-proposal-decorators',
{ decoratorsBeforeExport: true },
],
],
},
warnOnUnsupportedTypeScriptVersion: false,
},
plugins: ['ember', '@cardstack/boxel'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:ember/recommended',
'plugin:ember/recommended-gts',
'plugin:prettier/recommended',
'plugin:qunit-dom/recommended',
'plugin:@cardstack/boxel/recommended',
],
rules: {
'@cardstack/boxel/template-missing-invokable': [
'error',
{ invokables: MISSING_INVOKABLES_CONFIG.invokables },
],
},
},
// node files
{
files: [
Expand Down
3 changes: 2 additions & 1 deletion packages/boxel-icons/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"@babel/plugin-transform-class-static-block": "^7.22.11",
"@babel/plugin-transform-typescript": "^7.22.15",
"@babel/runtime": "^7.22.11",
"@cardstack/eslint-plugin-boxel": "workspace:*",
"@lucide/lab": "^0.1.2",
"@tabler/icons": "^3.19.0",
"@embroider/addon-dev": "^5.0.0",
Expand All @@ -61,7 +62,7 @@
"ember-template-lint-plugin-prettier": "^5.0.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-ember": "^11.4.2",
"eslint-plugin-ember": "^12.5.0",
"eslint-plugin-n": "^15.6.0",
"eslint-plugin-prettier": "^5.0.0",
"http-server": "^14.1.1",
Expand Down
46 changes: 46 additions & 0 deletions packages/boxel-motion/addon/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,52 @@ module.exports = {
],
},
overrides: [
{
files: ['**/*.gts'],
parser: 'ember-eslint-parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
requireConfigFile: false,
babelOptions: {
plugins: [
[
'@babel/plugin-proposal-decorators',
{ decoratorsBeforeExport: true },
],
],
},
warnOnUnsupportedTypeScriptVersion: false,
},
plugins: ['ember'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:ember/recommended',
'plugin:ember/recommended-gts',
'plugin:prettier/recommended',
'plugin:qunit-dom/recommended',
],
rules: {
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
],
'prefer-const': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-this-alias': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'no-undef': 'off',
'ember/template-no-let-reference': 'off',
'ember/no-tracked-properties-from-args': 'off',
'ember/no-runloop': 'off',
'node/no-deprecated-api': 'off',
},
},
// node files
{
files: [
Expand Down
2 changes: 1 addition & 1 deletion packages/boxel-motion/addon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
"ember-template-lint-plugin-prettier": "^5.0.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-ember": "^11.4.2",
"eslint-plugin-ember": "^12.5.0",
"eslint-plugin-n": "^15.6.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-simple-import-sort": "^8.0.0",
Expand Down
1 change: 1 addition & 0 deletions packages/boxel-motion/addon/src/services/animations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export default class AnimationsService extends Service {
this.animationParticipantManager.clearSnapshots();
this.animationParticipantManager.snapshotBeforeRender();

// eslint-disable-next-line ember/no-runloop
scheduleOnce('afterRender', this, this.maybeTransition);
}
}
Expand Down
2 changes: 2 additions & 0 deletions packages/boxel-motion/addon/src/utils/scheduling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ export function afterRender() {
let promise = new Promise((resolve) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line ember/no-runloop
ticket = schedule('afterRender', resolve);
});
registerCancellation(promise, () => {
// eslint-disable-next-line ember/no-runloop
cancel(ticket);
});
return promise;
Expand Down
12 changes: 6 additions & 6 deletions packages/boxel-motion/test-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"@cardstack/boxel-motion": "workspace:*",
"@ember/optional-features": "^2.0.0",
"@ember/string": "^3.1.1",
"@ember/test-helpers": "^3.2.0",
"@ember/test-helpers": "^3.3.1",
"@embroider/compat": "^3.5.5",
"@embroider/core": "^3.4.14",
"@embroider/macros": "^1.16.5",
Expand All @@ -46,8 +46,8 @@
"@types/htmlbars-inline-precompile": "^3.0.3",
"@types/qunit": "^2.19.10",
"@types/rsvp": "^4.0.9",
"@typescript-eslint/eslint-plugin": "^5.48.1",
"@typescript-eslint/parser": "^5.48.1",
"@typescript-eslint/eslint-plugin": "^7.16.1",
"@typescript-eslint/parser": "^7.16.1",
"broccoli-asset-rev": "^3.0.0",
"concurrently": "^8.2.2",
"ember-a11y-testing": "^6.1.1",
Expand Down Expand Up @@ -81,9 +81,9 @@
"ember-template-imports": "^4.1.1",
"ember-template-lint": "^5.11.2",
"ember-try": "^2.0.0",
"eslint": "^8.52.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-ember": "^11.11.1",
"eslint-plugin-ember": "^12.5.0",
"eslint-plugin-n": "^16.2.0",
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-qunit": "^8.0.1",
Expand All @@ -96,7 +96,7 @@
"qunit-dom": "^2.0.0",
"tracked-built-ins": "^3.3.0",
"typescript": "~5.1.6",
"webpack": "^5.89.0"
"webpack": "^5.99.6"
},
"engines": {
"node": ">= 18"
Expand Down
53 changes: 53 additions & 0 deletions packages/boxel-ui/addon/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

const MISSING_INVOKABLES_CONFIG = require('../../../eslint/missing-invokables-config');

module.exports = {
root: true,
parser: '@typescript-eslint/parser',
Expand Down Expand Up @@ -43,6 +45,57 @@ module.exports = {
],
},
overrides: [
{
files: ['**/*.gts'],
parser: 'ember-eslint-parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
requireConfigFile: false,
babelOptions: {
plugins: [
[
'@babel/plugin-proposal-decorators',
{ decoratorsBeforeExport: true },
],
],
},
warnOnUnsupportedTypeScriptVersion: false,
},
plugins: ['ember', '@cardstack/boxel'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:ember/recommended',
'plugin:ember/recommended-gts',
'plugin:prettier/recommended',
'plugin:qunit-dom/recommended',
'plugin:@cardstack/boxel/recommended',
],
rules: {
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
],
'prefer-const': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-this-alias': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'no-undef': 'off',
'ember/template-no-let-reference': 'off',
'ember/no-tracked-properties-from-args': 'off',
'ember/no-runloop': 'off',
'node/no-deprecated-api': 'off',
'@cardstack/boxel/template-missing-invokable': [
'error',
{ invokables: MISSING_INVOKABLES_CONFIG.invokables },
],
},
},
// node files
{
files: [
Expand Down
3 changes: 2 additions & 1 deletion packages/boxel-ui/addon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"@babel/plugin-transform-class-static-block": "^7.22.11",
"@babel/plugin-transform-typescript": "^7.22.15",
"@babel/runtime": "^7.22.11",
"@cardstack/eslint-plugin-boxel": "workspace:*",
"@embroider/addon-dev": "^5.0.0",
"@embroider/macros": "^1.16.5",
"@rollup/plugin-babel": "^6.0.4",
Expand All @@ -90,7 +91,7 @@
"ember-template-lint-plugin-prettier": "^5.0.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-ember": "^11.4.2",
"eslint-plugin-ember": "^12.5.0",
"eslint-plugin-n": "^15.6.0",
"eslint-plugin-prettier": "^5.0.0",
"glimmer-scoped-css": "^0.6.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface Signature {
Element: HTMLDivElement;
}

// eslint-disable-next-line ember/no-empty-glimmer-component-classes
export default class BasicFitted extends Component<Signature> {
<template>
<div class='fitted-template' ...attributes>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ interface Signature {
Element: HTMLElement;
}

// eslint-disable-next-line ember/no-empty-glimmer-component-classes
export default class CardContentContainerUsage extends Component<Signature> {
<template>
<FreestyleUsage @name='CardContentContainer'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ interface Signature {
Element: SVGElement;
}

// eslint-disable-next-line ember/no-empty-glimmer-component-classes
export default class CircleSpinner extends Component<Signature> {
<template>
<IconCircle class='circle-spinner' ...attributes />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ interface Signature {
Element: HTMLElement;
}

// eslint-disable-next-line ember/no-empty-glimmer-component-classes
export default class GridContainerUsage extends Component<Signature> {
<template>
<FreestyleUsage @name='GridContainer'>
Expand Down
2 changes: 2 additions & 0 deletions packages/boxel-ui/addon/src/components/multi-select/usage.gts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ interface CheckBoxArgs {
Element: Element;
}

// eslint-disable-next-line ember/no-empty-glimmer-component-classes
class CheckboxIndicator extends Component<CheckBoxArgs> {
<template>
<div class='checkbox-indicator'>
Expand Down Expand Up @@ -83,6 +84,7 @@ interface AssigneePillArgs {
}

//Custom component for rendering dropdown items with enhanced design and functionality
// eslint-disable-next-line ember/no-empty-glimmer-component-classes
class AssigneePill extends Component<AssigneePillArgs> {
<template>
<span class='assignee-pill'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ export interface SelectedItemSignature {
Element: HTMLDivElement;
}

// eslint-disable-next-line ember/no-empty-glimmer-component-classes
class PhoneSelectedItem extends Component<SelectedItemSignature> {
<template>
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export default class RadioInput extends Component<Signature> {
white-space: nowrap;
}

/* Div container inside the fieldset component. Use \`display: contents\` to move
/* Div container inside the fieldset component. Use display: contents to move
these styles up when that css property is more widely available. */
.boxel-radio-fieldset__container {
display: flex;
Expand Down
1 change: 1 addition & 0 deletions packages/boxel-ui/addon/src/components/select/trigger.gts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export class BoxelTriggerWrapper extends Component<TriggerSignature> {
</template>
}

// eslint-disable-next-line ember/no-empty-glimmer-component-classes
export class BoxelSelectDefaultTrigger extends Component<TriggerSignature> {
<template>
<BoxelTriggerWrapper @placeholder={{@placeholder}} @select={{@select}}>
Expand Down
1 change: 1 addition & 0 deletions packages/boxel-ui/addon/src/components/switch/index.gts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface SwitchArgs {
onChange: () => void;
}

// eslint-disable-next-line ember/no-empty-glimmer-component-classes
export default class Switch extends Component<SwitchSiganture> {
<template>
<label
Expand Down
Loading