ESLint
Optional Integration
Our ESLint integration is not required to use the QUI component library.
Plugin
We provide an ESLint plugin that enforces best-practices for accessibility and component consumption. This guide assumes you've already set up and configured ESLint. If not, we recommend starting with the configs section below.
In Development
This plugin is currently in development. New rules will be added over time.
Installation
Our configs are provided in ESM format. You will need "type": "module" in your project's package.json to consume them.
pnpm add -D @qualcomm-ui/eslint-plugin-angular
npm i --save-dev @qualcomm-ui/eslint-plugin-angular
yarn add -D @qualcomm-ui/eslint-plugin-angular
Setup
Add the plugin to your ESLint configuration:
import {defineConfig} from "eslint/config"
import angularEslint from "angular-eslint"
import angularPlugin from "@qualcomm-ui/eslint-plugin-angular"
export default defineConfig([
// ...the rest of your config
{
// your component files
files: ["**/*.ts"],
extends: [...angularEslint.configs.tsRecommended, angularPlugin.config],
plugins: {"@angular-eslint": angularEslint.tsPlugin},
processor: angularEslint.processInlineTemplates,
},
{
// your component html files
files: ["**/*.html"],
extends: [angularEslint.configs.templateRecommended, angularPlugin.config],
},
])Rules
accessible-name
Enforces that certain QUI components have an aria-label or aria-labelledby attribute for accessibility.
Affected directives:
q-icon-buttonq-inline-icon-button
<!-- Invalid -->
<button q-icon-button icon="close"></button>
<!-- Valid -->
<button q-icon-button icon="close" aria-label="Close dialog"></button>
<button q-icon-button icon="close" aria-labelledby="close-label"></button>
<button q-icon-button icon="close" [attr.aria-label]="closeLabel"></button>Configs
We provide shared ESLint configurations to enforce consistent code style and quality across our internal projects. These packages wrap popular open source plugins and provide a common baseline that makes it easier for developers to move between codebases.
Overview
Two packages are available for Angular projects:
@qualcomm-ui/eslint-config-typescript- TypeScript rules@qualcomm-ui/eslint-config-angular- Angular-specific rules
Both packages use ESLint's flat config format.
Installation
pnpm add -D @qualcomm-ui/eslint-config-typescript @qualcomm-ui/eslint-config-angular eslint globals typescript-eslint
npm i --save-dev @qualcomm-ui/eslint-config-typescript @qualcomm-ui/eslint-config-angular eslint globals typescript-eslint
yarn add -D @qualcomm-ui/eslint-config-typescript @qualcomm-ui/eslint-config-angular eslint globals typescript-eslint
An example project featuring the complete configuration is available at https://github.com/qualcomm/qualcomm-ui-templates/tree/main/templates/angular-ssr
Skip ahead to the Example Configuration section for a complete setup, or continue reading to learn more about the individual configs.
Config Deep Dive
TypeScript Configs
The @qualcomm-ui/eslint-config-typescript package exports the following configurations:
| Config | Description |
|---|---|
base | Core TypeScript rules |
recommended | Includes all configs below |
styleGuide | Code style enforcement |
sortKeys | Object key ordering |
typeChecks | Type-aware linting rules |
namingConventions | Identifier naming patterns |
performance | Performance-related rules |
strictExports | Export restrictions |
jsdoc | JSDoc comment validation |
Angular Configs
The @qualcomm-ui/eslint-config-angular package exports the following configurations:
| Config | Description |
|---|---|
baseTypescript | Extends angular-eslint recommended rules |
typescript | QUI-specific TypeScript rules for Angular |
baseTemplate | Extends angular-eslint template recommended |
templatePrettier | Prettier integration for Angular templates |
templateAttributeOrder | Enforces consistent attribute ordering |
templateSelfClosingTags | Requires self-closing tags for void elements |
Type-Aware Rules
Both the TypeScript and Angular configs include type-aware rules that require type information. Files matched by your ESLint config's files patterns must be included in the nearest tsconfig.json (or referenced project if you're using project references).
If ESLint errors with "file not found in any configured project," ensure the file is included in your tsconfig's files or include patterns.
TypeScript Rules
The typescript config enforces these conventions:
@angular-eslint/prefer-signals- Require Angular signals for reactive state@angular-eslint/no-input-rename- Warn when renaming inputsno-restricted-globals- Bans browser globals (window,document,navigator,location,localStorage,sessionStorage) for SSR compatibility
The following rules are disabled to allow flexibility:
@angular-eslint/component-class-suffix- Component class naming not enforced@angular-eslint/directive-class-suffix- Directive class naming not enforced@angular-eslint/no-host-metadata-property- Host bindings in metadata allowed
Template Attribute Order
The templateAttributeOrder config enforces this order for template attributes:
<!-- 1. Structural directives -->
*ngIf="condition"
<!-- 2. Template references -->
#templateRef
<!-- 3. Attribute bindings -->
[attr.role]="role"
<!-- 4. Input bindings -->
[value]="value"
<!-- 5. Two-way bindings -->
[(ngModel)]="model"
<!-- 6. Output bindings -->
(click)="onClick()"Attributes within each group are sorted alphabetically.
Self-Closing Tags
The templateSelfClosingTags config requires self-closing syntax for components without content:
<!-- Disallowed -->
<q-text-input></q-text-input>
<!-- Required -->
<q-text-input />Example Configuration
Type-aware rules
This configuration uses type-aware linting. Learn more here about potential pitfalls.
Create an eslint.config.js file in your project root:
import {defineConfig} from "eslint/config"
import globals from "globals"
import * as tseslint from "typescript-eslint"
import quiEslintAngular from "@qualcomm-ui/eslint-config-angular"
import quiEslintTs from "@qualcomm-ui/eslint-config-typescript"
import quiEslintPluginAngular from "@qualcomm-ui/eslint-plugin-angular"
const tsLanguageOptions = {
globals: globals.browser,
parser: tseslint.parser,
parserOptions: {
projectService: true,
},
}
export default defineConfig([
{
ignores: [
"**/.angular/",
"**/dist/",
"**/node_modules/",
"**/build/",
"**/coverage/",
"**/.turbo/",
"**/out/",
"**/out-tsc/",
"**/temp/",
"**/vite.config.ts.timestamp*",
],
},
{
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
},
// JS
{
extends: [
quiEslintTs.configs.base,
quiEslintTs.configs.sortKeys,
quiEslintTs.configs.styleGuide,
],
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
languageOptions: {globals: globals.browser},
},
// TS
{
extends: [
...quiEslintTs.configs.recommended,
quiEslintTs.configs.performance,
quiEslintTs.configs.strictExports,
],
// recommendation: scope these to your source files in your package(s).
files: ["**/*.{ts,tsx}"],
languageOptions: tsLanguageOptions,
},
// Angular
{
extends: [
...quiEslintTs.configs.recommended,
quiEslintTs.configs.performance,
quiEslintAngular.configs.baseTypescript,
quiEslintAngular.configs.typescript,
// optional: include the plugin as well
quiEslintPluginAngular.config,
],
// recommendation: scope these to your source files in your package(s).
files: ["**/*.ts"],
languageOptions: tsLanguageOptions,
},
{
extends: [
quiEslintAngular.configs.baseTemplate,
quiEslintAngular.configs.templatePrettier,
quiEslintAngular.configs.templateAttributeOrder,
quiEslintAngular.configs.templateSelfClosingTags,
// optional: include the plugin as well
quiEslintPluginAngular.config,
],
// recommendation: scope these to your source files in your package(s).
files: ["**/*.html"],
},
])