How to Configure the Linter Rules Programmatically in Instagit
You configure the linter rules programmatically by passing a custom RuleDescriptor[] array to the lint function, bypassing the default DEFAULT_RULE_DESCRIPTORS to disable, modify severity, or inject custom rules at runtime.
The Instagit design-linting engine from the google-labs-code/design.md repository exposes a composable, in-process API that lets you configure the linter rules programmatically without touching external configuration files. By supplying your own rule descriptors to the main lint helper, you gain full control over which validations run and how violations are reported.
Understanding the Rule Configuration Architecture
The programmatic configuration flow relies on three core components working together. First, the spec configuration in packages/cli/src/linter/spec-config.ts defines canonical sections and units via loadSpecConfig and getSpecConfig. Second, the rules index at packages/cli/src/linter/linter/rules/index.ts exports DEFAULT_RULE_DESCRIPTORS containing rule logic and default severities. Third, the lint runner in packages/cli/src/linter/lint.ts exposes the public lint function that accepts your custom rule array, while packages/cli/src/linter/linter/runner.ts handles the actual execution.
Loading the Spec Configuration
Before customizing rules, the system reads the DESIGN.md specification lazily. The getSpecConfig function in spec-config.ts provides access to canonical definitions that most rules reference during execution. This step happens automatically during the first rule execution, but you can preload it explicitly if your workflow requires early validation.
Customizing the Rule Set
Because the linter accepts a plain JavaScript array of descriptors, you manipulate the rule set using standard array operations. The helper toLintRule in the rules index converts descriptors into executable LintRule objects that the runner iterates over.
Disabling Default Rules
To remove a specific rule, filter it from the DEFAULT_RULE_DESCRIPTORS array before passing it to the linter.
import { DEFAULT_RULE_DESCRIPTORS } from '@/linter/rules/index';
import { lint } from '@/linter';
const customRuleSet = DEFAULT_RULE_DESCRIPTORS.filter(
d => d.name !== 'unknownKey'
);
const findings = await lint(state, { rules: customRuleSet });
Modifying Rule Severity
Copy an existing descriptor and replace its severity field to change how violations are reported without altering the underlying logic.
import { DEFAULT_RULE_DESCRIPTORS } from '@/linter/rules/index';
const adjustedDescriptors = DEFAULT_RULE_DESCRIPTORS.map(d => {
if (d.name === 'contrastCheck') {
// Downgrade from “error” to “warning”
return { ...d, severity: 'warning' as const };
}
return d;
});
const findings = await lint(state, { rules: adjustedDescriptors });
Adding Custom Rules
Create a new RuleDescriptor object adhering to the required shape—including name, description, severity, and a run function—and prepend or append it to your array.
import { DEFAULT_RULE_DESCRIPTORS, type RuleDescriptor } from '@/linter/rules/index';
import { lint } from '@/linter';
const dangerTokenRule: RuleDescriptor = {
name: 'dangerToken',
description: 'Disallow the use of a token called “danger”.',
severity: 'error',
run: (state) => {
const findings = [];
for (const token of Object.keys(state.tokens)) {
if (token === 'danger') {
findings.push({
path: [`tokens`, token],
message: 'Token “danger” is prohibited.',
});
}
}
return findings;
},
};
const customRuleSet: RuleDescriptor[] = [
dangerTokenRule,
...DEFAULT_RULE_DESCRIPTORS,
];
const findings = await lint(state, { rules: customRuleSet });
Executing the Linter with Custom Rules
The public lint function in packages/cli/src/linter/lint.ts accepts a DesignSystemState and an optional configuration object. If you omit the rules property, the linter uses DEFAULT_RULES; otherwise, it executes your custom list in the exact order provided.
import { missingPrimaryRule, sectionOrderRule } from '@/linter/rules/index';
import { lint } from '@/linter';
// Run only a subset of rules
const minimalSet = [missingPrimaryRule, sectionOrderRule];
const findings = await lint(state, { rules: minimalSet });
The CLI entry point in packages/cli/src/commands/lint.ts demonstrates this pattern by forwarding command-line flags to the same programmatic API, confirming that all configuration happens in-process without environment variables.
Summary
- Rule descriptors are plain JavaScript objects exported from
DEFAULT_RULE_DESCRIPTORSinpackages/cli/src/linter/linter/rules/index.ts. - Configuration happens at runtime by passing a
RuleDescriptor[]array to thelintfunction inpackages/cli/src/linter/lint.ts. - Customization supports disabling rules via array filtering, changing severity by modifying the
severityproperty, and adding custom logic with new descriptor objects. - Execution preserves the order of your array and requires no external configuration files.
Frequently Asked Questions
How do I disable a specific linter rule without modifying the source code?
Filter the DEFAULT_RULE_DESCRIPTORS array using the standard JavaScript filter method to exclude the rule by name, then pass the resulting array to the lint function. This happens entirely in memory and does not affect the default configuration file.
Can I change a rule severity from error to warning programmatically?
Yes. Map over DEFAULT_RULE_DESCRIPTORS and return a new object with the severity property changed to 'warning' for the specific rule you want to adjust. The linter uses this value during execution to determine how to report violations.
What is the difference between RuleDescriptor and LintRule?
A RuleDescriptor is the static configuration object containing metadata and a run function, while a LintRule is the executable version created internally by the toLintRule helper. You typically pass descriptors to the lint function, which converts them automatically before the runner in runner.ts executes them.
Can I run the linter with only my custom rules and ignore the defaults?
Absolutely. Instead of spreading DEFAULT_RULE_DESCRIPTORS, construct an array containing only your custom rule descriptors (or imported individual rules) and pass that directly to the lint function. The linter will execute only the rules you provide, in the order you specify.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →