Flexible configuration system supporting presets, custom rules, plugins, file-specific overrides, and dynamic configuration resolution.
System for loading and processing linting configuration from various sources.
/**
* Load project configuration from working directory
* @param workingDir - Directory to search for configuration files
* @param options - Additional configuration options
* @returns Complete project configuration
*/
function getProjectConfig(workingDir: string, options?: ConfigOptions): Promise<ProjectConfig>;
/**
* Parse inline rule specification
* @param ruleString - Rule specification like "rule-name:error" or "rule-name:[error, {config}]"
* @returns Parsed rule name and configuration
*/
function getRuleFromString(ruleString: string): { name: string; config: RuleConfig };
interface ConfigOptions {
/** Path to specific configuration file */
configPath?: string;
/** Console for logging configuration issues */
console?: Console;
/** Additional configuration overrides */
rule?: string;
}Complete structure of a project's linting configuration.
interface ProjectConfig {
/** Rule configurations */
rules: Record<string, RuleConfig>;
/** Base configurations to extend */
extends?: string | string[];
/** Plugin packages to load */
plugins?: string[];
/** File patterns to ignore */
ignore?: string[];
/** File-specific configuration overrides */
overrides?: Override[];
/** Output formatting configuration */
format?: FormatConfig;
/** Report unused disable directives */
reportUnusedDisableDirectives?: boolean;
/** Check HBS template literals in JS files */
checkHbsTemplateLiterals?: boolean;
/** Loaded rule classes (internal, not configuration) */
loadedRules?: Record<string, typeof Rule>;
/** Available configurations (internal, not configuration) */
loadedConfigurations?: Record<string, any>;
}Configuration for individual rules.
interface RuleConfig {
/** Rule severity: -1=todo, 0=ignore, 1=warn, 2=error */
severity: -1 | 0 | 1 | 2;
/** Rule-specific configuration options */
config?: any;
}
// Shorthand syntax support
type RuleConfigShorthand =
| "off" | 0 // Disable rule
| "warn" | 1 // Warning level
| "error" | 2 // Error level
| -1 // Todo level (numeric only, no "todo" string)
| [number, any] // [severity, config]
| [string, any]; // [severity_string, config] - only "off", "warn", "error"File-specific configuration overrides.
interface Override {
/** File pattern(s) to match */
files: string | string[];
/** Rule configurations for matched files */
rules: Record<string, RuleConfig>;
}Output formatting options. Supports both single formatter and multiple formatters.
interface FormatConfig {
/** Single formatter configuration */
name?: string;
outputFile?: string;
/** OR Multiple formatters configuration */
formatters?: FormatterSpec[];
}
interface FormatterSpec {
/** Formatter name */
name: string;
/** Output file path */
outputFile?: string;
}Supported configuration file names and formats.
// Supported configuration file names (in order of precedence)
const ConfigFileNames = [
".template-lintrc.js", // JavaScript configuration
".template-lintrc.mjs", // ES module configuration
".template-lintrc.cjs" // CommonJS configuration
];
// Note: package.json configuration is NOT supportedUsage Examples:
// .template-lintrc.js
module.exports = {
// Extend base configurations
extends: ['recommended', 'a11y'],
// Plugin integration
plugins: ['@ember-template-lint/plugin-custom'],
// Rule configurations
rules: {
// Basic rule enabling
'no-bare-strings': 'error',
'require-valid-alt-text': 'warn',
// Rule with configuration
'no-implicit-this': ['error', {
allow: ['can', 'cannot', 'is-array']
}],
'quotes': ['warn', 'double'],
'attribute-indentation': ['error', {
indentSize: 2,
alignAttributesVertically: false
}],
// Disable specific rules
'no-html-comments': 'off',
// Set as TODO (tracked but not failing)
'no-curly-component-invocation': 'todo'
},
// File-specific overrides
overrides: [
{
files: ['**/*-test.{js,ts}', '**/*.stories.{js,ts}'],
rules: {
'no-bare-strings': 'off',
'require-valid-alt-text': 'off'
}
},
{
files: ['app/templates/application.hbs'],
rules: {
'no-implicit-this': 'off'
}
}
],
// Global ignore patterns
ignore: [
'dist/**',
'tmp/**',
'node_modules/**'
],
// Output formatting
format: {
name: 'pretty',
outputFile: 'lint-results.txt'
},
// Global options
reportUnusedDisableDirectives: true,
checkHbsTemplateLiterals: true
};Pre-configured rule collections for common use cases.
// Available preset configurations
interface PresetConfigurations {
/** Core recommended rules for all Ember applications */
"recommended": PresetConfig;
/** Accessibility-focused rules for inclusive applications */
"a11y": PresetConfig;
/** Code style and formatting rules */
"stylistic": PresetConfig;
/** Legacy compatibility rules for Ember 5.x projects */
"5-x-recommended": PresetConfig;
}
interface PresetConfig {
rules: Record<string, RuleConfig>;
plugins?: string[];
}Preset usage examples:
// Extending single preset
module.exports = {
extends: 'recommended'
};
// Extending multiple presets
module.exports = {
extends: ['recommended', 'a11y', 'stylistic'],
// Override specific preset rules
rules: {
'no-bare-strings': 'warn' // Override from 'error' in recommended
}
};
// Custom preset creation
module.exports = {
extends: 'recommended',
rules: {
// Add custom rules not in recommended
'no-inline-styles': 'error',
'require-button-type': 'error',
// Relax some recommended rules
'no-html-comments': 'warn'
}
};Loading and configuring custom plugins.
interface Plugin {
/** Plugin name */
name: string;
/** Additional rules provided by plugin */
rules?: Record<string, typeof Rule>;
/** Additional configurations provided by plugin */
configurations?: Record<string, PresetConfig>;
/** Plugin initialization function */
init?: (options: PluginOptions) => void;
}
interface PluginOptions {
workingDir: string;
console: Console;
}Plugin usage examples:
// Loading plugins
module.exports = {
plugins: [
'@ember-template-lint/plugin-accessibility',
'ember-template-lint-plugin-custom',
'./local-plugin.js' // Local plugin file
],
// Using plugin rules
rules: {
'accessibility/no-redundant-alt': 'error',
'custom/special-rule': 'warn'
}
};
// Local plugin example (local-plugin.js)
module.exports = {
name: 'local-custom-plugin',
rules: {
'my-custom-rule': require('./rules/my-custom-rule')
},
configurations: {
'my-preset': {
rules: {
'my-custom-rule': 'error'
}
}
}
};Runtime configuration resolution and EditorConfig integration.
interface ConfigResolver {
/** Get EditorConfig settings for a file */
editorConfig?(): EditorConfigSettings;
/** Custom configuration resolution */
resolveConfig?(filePath: string): Partial<ProjectConfig>;
}
interface EditorConfigSettings {
indent_style?: 'tab' | 'space';
indent_size?: number;
tab_width?: number;
end_of_line?: 'lf' | 'crlf' | 'cr';
charset?: string;
trim_trailing_whitespace?: boolean;
insert_final_newline?: boolean;
}Dynamic configuration example:
// Advanced configuration with dynamic resolution
module.exports = {
extends: 'recommended',
rules: {
// Use EditorConfig for indentation rules
'attribute-indentation': ['error', 'editorconfig'],
'block-indentation': ['error', 'editorconfig']
},
overrides: [
{
files: ['**/*.{js,ts}'],
rules: {
// Check template literals in JavaScript files
'no-bare-strings': 'error'
}
}
]
};
// Programmatic configuration
const Linter = require('ember-template-lint');
const linter = new Linter({
configResolver: {
editorConfig() {
// Custom EditorConfig resolution
return {
indent_size: 4,
indent_style: 'space'
};
}
}
});Configuration validation and error reporting.
// Configuration validation errors
interface ConfigError extends Error {
name: 'ConfigError';
message: string;
filePath?: string;
code?: 'INVALID_CONFIG' | 'PLUGIN_NOT_FOUND' | 'RULE_NOT_FOUND';
}
// Common configuration issues
const ConfigIssues = {
INVALID_CONFIG: "Configuration file contains invalid syntax",
PLUGIN_NOT_FOUND: "Specified plugin could not be loaded",
RULE_NOT_FOUND: "Referenced rule is not available",
INVALID_RULE_CONFIG: "Rule configuration is invalid",
INVALID_OVERRIDE: "Override configuration is malformed"
};