Skip to content

Commit

Permalink
feat(builder): added outputFile option (#587)
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesHenry committed Jul 11, 2021
1 parent e82c687 commit 420734b
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 5 deletions.
65 changes: 65 additions & 0 deletions packages/builder/src/lint.impl.spec.ts
Expand Up @@ -3,6 +3,16 @@ import type { ESLint } from 'eslint';
import { resolve } from 'path';
import type { Schema } from './schema';

// If we use esm here we get `TypeError: Cannot redefine property: writeFileSync`
// eslint-disable-next-line @typescript-eslint/no-var-requires
const fs = require('fs');
jest.spyOn(fs, 'writeFileSync').mockImplementation();

const mockCreateDirectory = jest.fn();
jest.mock('./utils/create-directory', () => ({
createDirectory: mockCreateDirectory,
}));

const mockFormatter = {
format: jest
.fn()
Expand Down Expand Up @@ -58,6 +68,7 @@ function createValidRunBuilderOptions(
maxWarnings: -1,
silent: false,
ignorePath: null,
outputFile: null,
...additionalOptions,
};
}
Expand Down Expand Up @@ -132,6 +143,7 @@ describe('Linter Builder', () => {
force: false,
silent: false,
maxWarnings: -1,
outputFile: null,
ignorePath: null,
}),
mockContext,
Expand All @@ -152,6 +164,7 @@ describe('Linter Builder', () => {
force: false,
silent: false,
maxWarnings: -1,
outputFile: null,
ignorePath: null,
},
);
Expand Down Expand Up @@ -583,4 +596,56 @@ describe('Linter Builder', () => {
'Found 1 warnings, which exceeds your configured limit (0). Either increase your maxWarnings limit or fix some of the lint warnings.',
);
});

it('should attempt to write the lint results to the output file, if specified', async () => {
mockReports = [
{
errorCount: 2,
warningCount: 4,
results: [],
messages: [],
usedDeprecatedRules: [],
},
{
errorCount: 3,
warningCount: 6,
results: [],
messages: [],
usedDeprecatedRules: [],
},
];
setupMocks();
await lintExecutor(
createValidRunBuilderOptions({
eslintConfig: './.eslintrc.json',
lintFilePatterns: ['includedFile1'],
format: 'json',
silent: true,
force: false,
outputFile: 'a/b/c/outputFile1',
}),
mockContext,
);
expect(mockCreateDirectory).toHaveBeenCalledWith('/root/a/b/c');
expect(fs.writeFileSync).toHaveBeenCalledWith(
'/root/a/b/c/outputFile1',
mockFormatter.format(mockReports),
);
});

it('should not attempt to write the lint results to the output file, if not specified', async () => {
setupMocks();
jest.spyOn(fs, 'writeFileSync').mockImplementation();
await lintExecutor(
createValidRunBuilderOptions({
eslintConfig: './.eslintrc.json',
lintFilePatterns: ['includedFile1'],
format: 'json',
silent: true,
force: false,
}),
mockContext,
);
expect(fs.writeFileSync).not.toHaveBeenCalled();
});
});
21 changes: 16 additions & 5 deletions packages/builder/src/lint.impl.ts
@@ -1,7 +1,9 @@
import type { ExecutorContext } from '@nrwl/devkit';
import type { ESLint } from 'eslint';
import path from 'path';
import { writeFileSync } from 'fs';
import { dirname, join, resolve } from 'path';
import type { Schema } from './schema';
import { createDirectory } from './utils/create-directory';
import { lint, loadESLint } from './utils/eslint-utils';

export default async function run(
Expand Down Expand Up @@ -38,7 +40,7 @@ export default async function run(
* eslint automatically resolve the `.eslintrc` files in each folder.
*/
const eslintConfigPath = options.eslintConfig
? path.resolve(workspaceRoot, options.eslintConfig)
? resolve(workspaceRoot, options.eslintConfig)
: undefined;

const lintResults: ESLint.LintResult[] = await lint(
Expand Down Expand Up @@ -92,10 +94,19 @@ export default async function run(
* formatters, such as checkstyle, can provide a valid output for the
* whole project being linted.
*
* Additionally, we want to always log because different formatters
* handled the "no results" case differently.
* Additionally, apart from when outputting to a file, we want to always
* log (even when no results) because different formatters handled the
* "no results" case differently.
*/
console.info(formatter.format(finalLintResults));
const formattedResults = formatter.format(finalLintResults);

if (options.outputFile) {
const pathToOutputFile = join(context.root, options.outputFile);
createDirectory(dirname(pathToOutputFile));
writeFileSync(pathToOutputFile, formattedResults);
} else {
console.info(formattedResults);
}

if (hasWarningsToPrint && printInfo) {
console.warn('Lint warnings found in the listed files.\n');
Expand Down
1 change: 1 addition & 0 deletions packages/builder/src/schema.d.ts
Expand Up @@ -13,6 +13,7 @@ export interface Schema extends JsonObject {
cacheStrategy: 'content' | 'metadata' | null;
eslintConfig: string | null;
ignorePath: string | null;
outputFile: string | null;
}

type Formatter =
Expand Down
4 changes: 4 additions & 0 deletions packages/builder/src/schema.json
Expand Up @@ -22,6 +22,10 @@
"type": "string",
"description": "Path to the cache file or directory."
},
"outputFile": {
"type": "string",
"description": "File to write report to instead of the console."
},
"cacheStrategy": {
"type": "string",
"description": "Strategy to use for detecting changed files in the cache.",
Expand Down
20 changes: 20 additions & 0 deletions packages/builder/src/utils/create-directory.ts
@@ -0,0 +1,20 @@
import { dirname } from 'path';
import { mkdirSync, statSync } from 'fs';

export function createDirectory(directoryPath: string): void {
const parentPath = dirname(directoryPath);
if (!directoryExists(parentPath)) {
createDirectory(parentPath);
}
if (!directoryExists(directoryPath)) {
mkdirSync(directoryPath);
}
}

function directoryExists(name: string): boolean {
try {
return statSync(name).isDirectory();
} catch {
return false;
}
}

0 comments on commit 420734b

Please sign in to comment.