Some checks failed
ci/woodpecker/push/ci Pipeline failed
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
182 lines
4.9 KiB
TypeScript
182 lines
4.9 KiB
TypeScript
import type { QualityProfile, RailsConfig } from './types.js';
|
|
|
|
const PROFILE_TO_MAX_WARNINGS: Record<QualityProfile, number> = {
|
|
strict: 0,
|
|
standard: 10,
|
|
minimal: 50,
|
|
};
|
|
|
|
const PROFILE_TO_LINE_LENGTH: Record<QualityProfile, number> = {
|
|
strict: 100,
|
|
standard: 110,
|
|
minimal: 120,
|
|
};
|
|
|
|
export function eslintTemplate(profile: QualityProfile): string {
|
|
return `${JSON.stringify(
|
|
{
|
|
root: true,
|
|
parser: '@typescript-eslint/parser',
|
|
plugins: ['@typescript-eslint'],
|
|
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
|
|
env: {
|
|
node: true,
|
|
es2022: true,
|
|
},
|
|
parserOptions: {
|
|
ecmaVersion: 'latest',
|
|
sourceType: 'module',
|
|
},
|
|
rules: {
|
|
'no-console': profile === 'strict' ? 'error' : 'warn',
|
|
'@typescript-eslint/no-explicit-any':
|
|
profile === 'minimal' ? 'off' : profile === 'strict' ? 'error' : 'warn',
|
|
'@typescript-eslint/explicit-function-return-type': profile === 'strict' ? 'warn' : 'off',
|
|
'max-lines-per-function': [
|
|
profile === 'minimal' ? 'off' : 'warn',
|
|
{
|
|
max: profile === 'strict' ? 60 : 100,
|
|
skipBlankLines: true,
|
|
skipComments: true,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
null,
|
|
2,
|
|
)}\n`;
|
|
}
|
|
|
|
export function biomeTemplate(profile: QualityProfile): string {
|
|
return `${JSON.stringify(
|
|
{
|
|
$schema: 'https://biomejs.dev/schemas/1.8.3/schema.json',
|
|
formatter: {
|
|
enabled: true,
|
|
indentStyle: 'space',
|
|
indentWidth: 2,
|
|
lineWidth: PROFILE_TO_LINE_LENGTH[profile],
|
|
},
|
|
linter: {
|
|
enabled: true,
|
|
rules: {
|
|
recommended: true,
|
|
suspicious: {
|
|
noConsole: profile === 'strict' ? 'error' : 'warn',
|
|
},
|
|
complexity: {
|
|
noExcessiveCognitiveComplexity:
|
|
profile === 'strict' ? 'warn' : profile === 'standard' ? 'info' : 'off',
|
|
},
|
|
},
|
|
},
|
|
javascript: {
|
|
formatter: {
|
|
quoteStyle: 'single',
|
|
trailingCommas: 'all',
|
|
},
|
|
},
|
|
},
|
|
null,
|
|
2,
|
|
)}\n`;
|
|
}
|
|
|
|
export function pyprojectSection(profile: QualityProfile): string {
|
|
const lineLength = PROFILE_TO_LINE_LENGTH[profile];
|
|
return [
|
|
'# >>> mosaic-quality-rails >>>',
|
|
'[tool.ruff]',
|
|
`line-length = ${lineLength}`,
|
|
'target-version = "py311"',
|
|
'',
|
|
'[tool.ruff.lint]',
|
|
'select = ["E", "F", "I", "UP", "B"]',
|
|
`ignore = ${profile === 'minimal' ? '[]' : '["E501"]'}`,
|
|
'',
|
|
'[tool.black]',
|
|
`line-length = ${lineLength}`,
|
|
'',
|
|
'# <<< mosaic-quality-rails <<<',
|
|
'',
|
|
].join('\n');
|
|
}
|
|
|
|
export function rustfmtTemplate(profile: QualityProfile): string {
|
|
const maxWidth = PROFILE_TO_LINE_LENGTH[profile];
|
|
const useSmallHeuristics = profile === 'strict' ? 'Max' : 'Default';
|
|
|
|
return [
|
|
`max_width = ${maxWidth}`,
|
|
`use_small_heuristics = "${useSmallHeuristics}"`,
|
|
`imports_granularity = "${profile === 'minimal' ? 'Crate' : 'Module'}"`,
|
|
`group_imports = "${profile === 'strict' ? 'StdExternalCrate' : 'Preserve'}"`,
|
|
'',
|
|
].join('\n');
|
|
}
|
|
|
|
function resolveHookCommands(config: RailsConfig): string[] {
|
|
const commands: string[] = [];
|
|
|
|
if (config.kind === 'node') {
|
|
if (config.linters.some((linter) => linter.toLowerCase() === 'eslint')) {
|
|
commands.push('pnpm lint');
|
|
}
|
|
if (config.linters.some((linter) => linter.toLowerCase() === 'biome')) {
|
|
commands.push('pnpm biome check .');
|
|
}
|
|
if (config.formatters.some((formatter) => formatter.toLowerCase() === 'prettier')) {
|
|
commands.push('pnpm prettier --check .');
|
|
}
|
|
commands.push('pnpm test --if-present');
|
|
}
|
|
|
|
if (config.kind === 'python') {
|
|
commands.push('ruff check .');
|
|
commands.push('black --check .');
|
|
}
|
|
|
|
if (config.kind === 'rust') {
|
|
commands.push('cargo fmt --check');
|
|
commands.push('cargo clippy --all-targets --all-features -- -D warnings');
|
|
}
|
|
|
|
if (commands.length === 0) {
|
|
commands.push('echo "No quality commands configured for this project kind"');
|
|
}
|
|
|
|
return commands;
|
|
}
|
|
|
|
export function preCommitHookTemplate(config: RailsConfig): string {
|
|
const commands = resolveHookCommands(config)
|
|
.map((command) => `${command} || exit 1`)
|
|
.join('\n');
|
|
|
|
return [
|
|
'#!/usr/bin/env sh',
|
|
'set -eu',
|
|
'',
|
|
'echo "[quality-rails] Running pre-commit checks..."',
|
|
commands,
|
|
'echo "[quality-rails] Checks passed."',
|
|
'',
|
|
].join('\n');
|
|
}
|
|
|
|
export function prChecklistTemplate(profile: QualityProfile): string {
|
|
return [
|
|
'# Code Review Checklist',
|
|
'',
|
|
`Profile: **${profile}**`,
|
|
'',
|
|
'- [ ] Requirements mapped to tests',
|
|
'- [ ] Error handling covers unhappy paths',
|
|
'- [ ] Lint and typecheck are clean',
|
|
'- [ ] Test suite passes',
|
|
'- [ ] Security-sensitive paths reviewed',
|
|
`- [ ] Warnings count <= ${PROFILE_TO_MAX_WARNINGS[profile]}`,
|
|
'',
|
|
].join('\n');
|
|
}
|