Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for loading custom formatter from package #6228

Merged
merged 9 commits into from
Jul 29, 2022
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ node_modules
.eslintcache
yarn.lock
.vscode/settings.json
.idea
2 changes: 1 addition & 1 deletion docs/user-guide/usage/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Options are:
- `unix` - generates messages like a C compiler, so that tools like Emacs' _Compilation mode_ can hyperlink them
- `verbose` - extends `string` to include a list of checked files and a tally for each rule

The `formatter` Node.js API option can also accept a function, whereas the `--custom-formatter` CLI flag accepts a path to a JS file exporting one. The function in both cases must fit the signature described in the [Developer Guide](../../developer-guide/formatters.md).
The `formatter` Node.js API option can also accept a function, whereas the `--custom-formatter` CLI flag accepts a path (either a filesystem path or a dependency) to a JS file exporting one. The function in both cases must fit the signature described in the [Developer Guide](../../developer-guide/formatters.md).

## `cache`

Expand Down
1 change: 1 addition & 0 deletions lib/__tests__/__snapshots__/cli.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ exports[`CLI --help 1`] = `
--custom-formatter

Path to a JS file exporting a custom formatting function.
The file can either be a filesystem path, or a file to load from a dependency.
meriouma marked this conversation as resolved.
Show resolved Hide resolved

--quiet, -q

Expand Down
14 changes: 14 additions & 0 deletions lib/__tests__/cli.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -333,4 +333,18 @@ describe('CLI', () => {

expect(process.stdout.write).toHaveBeenCalledTimes(0);
});

it('--custom-formatter', async () => {
await cli([
`--custom-formatter=${fixturesPath('formatters/customFormatter')}`,
'--config',
fixturesPath('default-severity-warning.json'),
fixturesPath('empty-block.css'),
]);

expect(process.stdout.write).toHaveBeenCalledTimes(1);
const output = process.stdout.write.mock.calls[0][0];

expect(output).toBe('Custom formatter reports 1 warning(s).');
});
});
3 changes: 3 additions & 0 deletions lib/__tests__/fixtures/formatters/customFormatter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = function customFormatter(messages) {
return `Custom formatter reports ${messages.length} warning(s).`;
};
34 changes: 34 additions & 0 deletions lib/__tests__/resolveCustomFormatter.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
jest.mock('fs');
jest.mock('path');

const fs = require('fs');
const path = require('path');
const resolveCustomFormatter = require('../resolveCustomFormatter');

describe('resolveCustomFormatter', () => {
it('should return absolute path when provided path is a file path', () => {
const aRelativePath = 'a/relative/path';
const expected = `/cwd/${aRelativePath}`;

path.resolve.mockReturnValue(expected);
path.isAbsolute.mockReturnValue(false);
fs.existsSync.mockReturnValue(true);

const result = resolveCustomFormatter(aRelativePath);

expect(result).toEqual(expected);
});

it('should return provided path when path is neither absolute nor relative', () => {
const aModulePath = '@stylelint/prettier-config/index.js';

fs.existsSync.mockReturnValue(false);

const result = resolveCustomFormatter(aModulePath);

const realPathModule = jest.requireActual('path');
const expectedPath = realPathModule.join('node_modules', aModulePath);
JounQin marked this conversation as resolved.
Show resolved Hide resolved

expect(result.endsWith(expectedPath)).toBe(true);
});
});
6 changes: 3 additions & 3 deletions lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const printConfig = require('./printConfig');
const resolveFrom = require('resolve-from');
const standalone = require('./standalone');
const writeOutputFile = require('./writeOutputFile');
const resolveCustomFormatter = require('./resolveCustomFormatter');

const EXIT_CODE_ERROR = 2;

Expand Down Expand Up @@ -178,6 +179,7 @@ const meowOptions = {
--custom-formatter

Path to a JS file exporting a custom formatting function.
The file can either be a filesystem path, or a file to load from a dependency.

--quiet, -q

Expand Down Expand Up @@ -338,9 +340,7 @@ module.exports = async (argv) => {
let formatter = cli.flags.formatter;

if (cli.flags.customFormatter) {
const customFormatter = path.isAbsolute(cli.flags.customFormatter)
? cli.flags.customFormatter
: path.join(process.cwd(), cli.flags.customFormatter);
const customFormatter = resolveCustomFormatter(cli.flags.customFormatter);

JounQin marked this conversation as resolved.
Show resolved Hide resolved
formatter = require(customFormatter);
}
Expand Down
16 changes: 16 additions & 0 deletions lib/resolveCustomFormatter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const fs = require('fs');
const path = require('path');

/**
* @param {string} formatterPath
* @returns {string}
*/
module.exports = function resolveCustomFormatter(formatterPath) {
const resolvedPath = path.resolve(formatterPath);

if (fs.existsSync(resolvedPath)) {
return resolvedPath;
}

return require.resolve(formatterPath);
};