Skip to content
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
24 changes: 11 additions & 13 deletions src/bin/matrixai-lint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ function printDomainList(
): void {
logger.info('Available lint domains:');
for (const domainInfo of domains) {
logger.info(`- ${domainInfo.domain}: ${domainInfo.description}`);
logger.info(
`Domain info: domain=${domainInfo.domain} description=${domainInfo.description}`,
);
}
}

Expand All @@ -106,24 +108,20 @@ function printExplain(
): void {
logger.info('[matrixai-lint] Domain execution plan:');
for (const decision of decisions) {
logger.info(`[matrixai-lint] - domain: ${decision.domain}`);
logger.info(
`[matrixai-lint] selection: ${decision.selectionSource}${decision.explicitlyRequested ? ' (explicit)' : ''}`,
);
logger.info(`[matrixai-lint] domain=${decision.domain}`);
logger.info(
`[matrixai-lint] relevance: ${describeRelevance(decision)}`,
`[matrixai-lint] selection=${decision.selectionSource}${decision.explicitlyRequested ? ' (explicit)' : ''}`,
);
logger.info(`[matrixai-lint] relevance=${describeRelevance(decision)}`);
logger.info(
`[matrixai-lint] availability: ${describeAvailability(decision)}`,
`[matrixai-lint] availability=${describeAvailability(decision)}`,
);
if (decision.detectionError != null) {
logger.error(
`[matrixai-lint] detection-error: ${decision.detectionError}`,
`[matrixai-lint] detection-error=${decision.detectionError}`,
);
}
logger.info(
`[matrixai-lint] planned-action: ${decision.plannedAction}`,
);
logger.info(`[matrixai-lint] planned-action=${decision.plannedAction}`);
}
}

Expand Down Expand Up @@ -227,10 +225,10 @@ async function main(argv = process.argv) {
}

if (hadFailure) {
logger.error('[matrixai-lint] Linting failed.');
logger.error('[matrixai-lint] Linting failed.');
process.exit(1);
} else {
logger.info('[matrixai-lint] Linting passed.');
logger.info('[matrixai-lint] Linting passed.');
}
}

Expand Down
18 changes: 12 additions & 6 deletions src/domains/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ type LintDomainPlugin = {
) => LintDomainPluginResult);
};

function normalizeLogDetail(value: unknown): string {
return String(value)
.replace(/\r?\n+/g, ' | ')
.trim();
}

function createLintDomainRegistry(
plugins: readonly LintDomainPlugin[],
): Map<LintDomain, LintDomainPlugin> {
Expand Down Expand Up @@ -212,7 +218,7 @@ async function runLintDomainDecisions({
}

if (plannedAction === 'fail-detection') {
const message = `[matrixai-lint] - Domain "${domain}" failed unexpectedly.\n${detectionError ?? 'Unknown detection error.'}`;
const message = `[matrixai-lint] - Domain "${domain}" failed unexpectedly. ${normalizeLogDetail(detectionError ?? 'Unknown detection error.')}`;
logger.error(message);
hadFailure = true;
continue;
Expand All @@ -222,7 +228,7 @@ async function runLintDomainDecisions({
if (explicitlyRequested) {
const relevanceReason =
detection?.relevanceReason ?? 'No files matched in effective scope.';
const message = `[matrixai-lint] - Domain "${domain}" was explicitly requested, but no files matched. ${relevanceReason}`;
const message = `[matrixai-lint] - Domain "${domain}" was explicitly requested, but no files matched. ${relevanceReason}`;
logger.warn(message);
}
continue;
Expand All @@ -232,7 +238,7 @@ async function runLintDomainDecisions({
const unavailableReason =
detection?.unavailableReason ??
`Tooling for domain "${domain}" is not available.`;
const message = `[matrixai-lint] - Domain "${domain}" cannot run. ${unavailableReason}`;
const message = `[matrixai-lint] - Domain "${domain}" cannot run. ${unavailableReason}`;
logger.error(message);
hadFailure = true;
continue;
Expand All @@ -242,7 +248,7 @@ async function runLintDomainDecisions({
const unavailableReason =
detection?.unavailableReason ??
`Tooling for domain "${domain}" is not available.`;
const message = `[matrixai-lint] - Domain "${domain}" skipped. ${unavailableReason}`;
const message = `[matrixai-lint] - Domain "${domain}" skipped. ${unavailableReason}`;
logger.warn(message);
continue;
}
Expand All @@ -253,7 +259,7 @@ async function runLintDomainDecisions({
}

if (detection == null) {
const message = `[matrixai-lint] - Domain "${domain}" is missing detection metadata.`;
const message = `[matrixai-lint] - Domain "${domain}" is missing detection metadata.`;
logger.error(message);
hadFailure = true;
continue;
Expand All @@ -265,7 +271,7 @@ async function runLintDomainDecisions({
hadFailure = true;
}
} catch (err) {
const message = `[matrixai-lint] - Domain "${domain}" failed unexpectedly.\n${String(err)}`;
const message = `[matrixai-lint] - Domain "${domain}" failed unexpectedly. ${normalizeLogDetail(err)}`;
logger.error(message);
hadFailure = true;
}
Expand Down
13 changes: 12 additions & 1 deletion src/domains/eslint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import {
import * as utils from '../utils.js';
import { resolveLintConfig } from '../config.js';

function normalizeLogDetail(value: unknown): string {
return String(value)
.replace(/\r?\n+/g, ' | ')
.trim();
}

const ESLINT_FILE_EXTENSIONS = [
'.js',
'.mjs',
Expand Down Expand Up @@ -109,7 +115,12 @@ function createESLintDomainPlugin(): LintDomainPlugin {

return { hadFailure: hadLintingErrors };
} catch (err) {
logger.error(`ESLint failed: \n${err}`);
const errorDetail = normalizeLogDetail(err);
logger.error(
errorDetail.length > 0
? `ESLint failed. ${errorDetail}`
: 'ESLint failed.',
);
return { hadFailure: true };
}
},
Expand Down
27 changes: 23 additions & 4 deletions src/domains/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ const DEFAULT_MARKDOWN_SEARCH_ROOTS = [
'./docs',
];

function normalizeLogDetail(value: unknown): string {
return String(value)
.replace(/\r?\n+/g, ' | ')
.trim();
}

function collectMarkdownFilesFromScope(patterns: readonly string[]): string[] {
const matchedRelativeFiles = resolveFilesFromPatterns(
patterns,
Expand Down Expand Up @@ -92,7 +98,9 @@ function createMarkdownDomainPlugin({

try {
if (prettierBin) {
logger.info(` ${prettierBin} \n ${prettierArgs.join('\n' + ' ')}`);
logger.info(
`Running prettier command: ${process.execPath} ${prettierBin} ${prettierArgs.join(' ')}`,
);
childProcess.execFileSync(
process.execPath,
[prettierBin, ...prettierArgs],
Expand All @@ -104,7 +112,9 @@ function createMarkdownDomainPlugin({
},
);
} else {
logger.info('prettier ' + prettierArgs.join('\n' + ' '));
logger.info(
`Running prettier command: prettier ${prettierArgs.join(' ')}`,
);
childProcess.execFileSync('prettier', prettierArgs, {
stdio: 'inherit',
windowsHide: true,
Expand All @@ -114,10 +124,19 @@ function createMarkdownDomainPlugin({
});
}
} catch (err) {
const errorDetail = normalizeLogDetail(err);
if (!fix) {
logger.error('Prettier check failed.');
logger.error(
errorDetail.length > 0
? `Prettier check failed. ${errorDetail}`
: 'Prettier check failed.',
);
} else {
logger.error('Prettier write failed. ' + err);
logger.error(
errorDetail.length > 0
? `Prettier write failed. ${errorDetail}`
: 'Prettier write failed.',
);
}

return { hadFailure: true };
Expand Down
17 changes: 15 additions & 2 deletions src/domains/shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ const platform = os.platform();

const SHELL_FILE_EXTENSIONS = ['.sh'] as const;

function normalizeLogDetail(value: unknown): string {
return String(value)
.replace(/\r?\n+/g, ' | ')
.trim();
}

function resolveShellPatterns(
shellPatterns: readonly string[] | undefined,
defaultSearchRoots: readonly string[],
Expand Down Expand Up @@ -55,7 +61,9 @@ function createShellDomainPlugin({
}

logger.info('Running shellcheck:');
logger.info(' ' + ['shellcheck', ...matchedFiles].join(' '));
logger.info(
`Running shellcheck command: shellcheck ${matchedFiles.join(' ')}`,
);

try {
childProcess.execFileSync('shellcheck', matchedFiles, {
Expand All @@ -68,7 +76,12 @@ function createShellDomainPlugin({

return { hadFailure: false };
} catch (err) {
logger.error('Shellcheck failed. ' + err);
const errorDetail = normalizeLogDetail(err);
logger.error(
errorDetail.length > 0
? `Shellcheck failed. ${errorDetail}`
: 'Shellcheck failed.',
);
return { hadFailure: true };
}
},
Expand Down
39 changes: 32 additions & 7 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ async function runESLint({
// PATH A - user supplied explicit globs
if (explicitGlobs?.length) {
logger.info('Linting with explicit patterns:');
explicitGlobs.forEach((g) => logger.info(' ' + g));
explicitGlobs.forEach((pattern) => {
logger.info(`Linting: ${pattern}`);
});

const eslint = new ESLint({
overrideConfigFile: configPath || defaultConfigPath,
Expand All @@ -93,12 +95,14 @@ async function runESLint({
const { forceInclude, tsconfigPaths } = lintConfig.domains.eslint;

if (tsconfigPaths.length === 0) {
logger.error('[matrixai-lint] No tsconfig.json files found.');
logger.error('[matrixai-lint] No tsconfig.json files found.');
return true;
}

logger.info(`Found ${tsconfigPaths.length} tsconfig.json files:`);
tsconfigPaths.forEach((p) => logger.info(' ' + p));
tsconfigPaths.forEach((tsconfigPath) => {
logger.info(`Using tsconfig: ${tsconfigPath}`);
});

const { files: patterns, ignore: ignorePats } = buildPatterns(
tsconfigPaths,
Expand All @@ -109,13 +113,15 @@ async function runESLint({

if (patterns.length === 0) {
logger.warn(
'[matrixai-lint] No ESLint targets were derived from configured tsconfig paths.',
'[matrixai-lint] No ESLint targets were derived from configured tsconfig paths.',
);
return false;
}

logger.info('Linting files:');
patterns.forEach((p) => logger.info(' ' + p));
patterns.forEach((pattern) => {
logger.info(`Linting: ${pattern}`);
});

const eslint = new ESLint({
overrideConfigFile: configPath || defaultConfigPath,
Expand Down Expand Up @@ -144,9 +150,28 @@ async function lintAndReport(
await ESLint.outputFixes(results);
}

const errorCount = results.reduce(
(sum, result) => sum + result.errorCount,
0,
);
const warningCount = results.reduce(
(sum, result) => sum + result.warningCount,
0,
);
logger.info(
`ESLint summary: files=${results.length} errors=${errorCount} warnings=${warningCount} fix=${fix ? 'on' : 'off'}`,
);

const formatter = await eslint.loadFormatter('stylish');
logger.info(formatter.format(results));
const hasErrors = results.some((r) => r.errorCount > 0);
const formattedOutput = await formatter.format(results);
for (const line of formattedOutput.split(/\r?\n/)) {
const normalizedLine = line.trim();
if (normalizedLine.length > 0) {
logger.info(`ESLint detail: ${normalizedLine}`);
}
}

const hasErrors = errorCount > 0;

return hasErrors;
}
Expand Down
2 changes: 1 addition & 1 deletion tests/bin/lint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ describe('matrixai-lint CLI domain semantics', () => {
);
expect(stderrWriteSpy).toHaveBeenCalledWith(
expect.stringContaining(
'INFO:matrixai-lint:[matrixai-lint] - domain: markdown',
'INFO:matrixai-lint:[matrixai-lint] domain=markdown',
),
);
expect(stderrWriteSpy).toHaveBeenCalledWith(
Expand Down
Loading