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

feat: add --check CLI option #64

Merged
merged 5 commits into from May 20, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Expand Up @@ -101,6 +101,10 @@ Outputs the name of each file right before it is proccessed. This can be useful

Prevent `git commit` if any files are fixed.

### `--check`

Check that files are correctly formatted, but don't format them. This is useful on CI to verify that all changed files in the current branch were correctly formatted.

<!-- Undocumented = Unsupported :D
### `--config`
Expand Down
13 changes: 11 additions & 2 deletions bin/pretty-quick.js
Expand Up @@ -32,8 +32,12 @@ const prettyQuickResult = prettyQuick(
console.log(`✗ Found ${chalk.bold('partially')} staged file ${file}.`);
},

onWriteFile: file => {
console.log(`✍️ Fixing up ${chalk.bold(file)}.`);
onProcessFile: file => {
if (args.check) {
console.log(`👀 Checking ${chalk.bold(file)}.`);
} else {
console.log(`✍️ Fixing up ${chalk.bold(file)}.`);
}
},

onExamineFile: file => {
Expand All @@ -56,5 +60,10 @@ if (prettyQuickResult.success) {
'✗ File had to be prettified and prettyQuick was set to bail mode.'
);
}
if (prettyQuickResult.errors.indexOf('CHECK_FAILED') !== -1) {
console.log(
'✗ Code style issues found in the above file(s). Forgot to run Prettier?'
);
}
process.exit(1); // ensure git hooks abort
}
60 changes: 30 additions & 30 deletions src/__tests__/scm-git.test.js
Expand Up @@ -150,63 +150,63 @@ describe('with git', () => {
expect(onFoundChangedFiles).toHaveBeenCalledWith(['./foo.js', './bar.md']);
});

test('calls onWriteFile with changed files', () => {
const onWriteFile = jest.fn();
test('calls onProcessFile with changed files', () => {
const onProcessFile = jest.fn();
mockGitFs();

prettyQuick('root', { since: 'banana', onWriteFile });
prettyQuick('root', { since: 'banana', onProcessFile });

expect(onWriteFile).toHaveBeenCalledWith('./foo.js');
expect(onWriteFile).toHaveBeenCalledWith('./bar.md');
expect(onWriteFile.mock.calls.length).toBe(2);
expect(onProcessFile).toHaveBeenCalledWith('./foo.js');
expect(onProcessFile).toHaveBeenCalledWith('./bar.md');
expect(onProcessFile.mock.calls.length).toBe(2);
});

test('calls onWriteFile with changed files for the given pattern', () => {
const onWriteFile = jest.fn();
test('calls onProcessFile with changed files for the given pattern', () => {
const onProcessFile = jest.fn();
mockGitFs();
prettyQuick('root', { pattern: '*.md', since: 'banana', onWriteFile });
expect(onWriteFile.mock.calls).toEqual([['./bar.md']]);
prettyQuick('root', { pattern: '*.md', since: 'banana', onProcessFile });
expect(onProcessFile.mock.calls).toEqual([['./bar.md']]);
});

test('calls onWriteFile with changed files for the given globstar pattern', () => {
const onWriteFile = jest.fn();
test('calls onProcessFile with changed files for the given globstar pattern', () => {
const onProcessFile = jest.fn();
mockGitFs();
prettyQuick('root', {
pattern: '**/*.md',
since: 'banana',
onWriteFile,
onProcessFile,
});
expect(onWriteFile.mock.calls).toEqual([['./bar.md']]);
expect(onProcessFile.mock.calls).toEqual([['./bar.md']]);
});

test('calls onWriteFile with changed files for the given extglob pattern', () => {
const onWriteFile = jest.fn();
test('calls onProcessFile with changed files for the given extglob pattern', () => {
const onProcessFile = jest.fn();
mockGitFs();
prettyQuick('root', {
pattern: '*.*(md|foo|bar)',
since: 'banana',
onWriteFile,
onProcessFile,
});
expect(onWriteFile.mock.calls).toEqual([['./bar.md']]);
expect(onProcessFile.mock.calls).toEqual([['./bar.md']]);
});

test('calls onWriteFile with changed files for an array of globstar patterns', () => {
const onWriteFile = jest.fn();
test('calls onProcessFile with changed files for an array of globstar patterns', () => {
const onProcessFile = jest.fn();
mockGitFs();
prettyQuick('root', {
pattern: ['**/*.foo', '**/*.md', '**/*.bar'],
since: 'banana',
onWriteFile,
onProcessFile,
});
expect(onWriteFile.mock.calls).toEqual([['./bar.md']]);
expect(onProcessFile.mock.calls).toEqual([['./bar.md']]);
});

test('writes formatted files to disk', () => {
const onWriteFile = jest.fn();
const onProcessFile = jest.fn();

mockGitFs();

prettyQuick('root', { since: 'banana', onWriteFile });
prettyQuick('root', { since: 'banana', onProcessFile });

expect(fs.readFileSync('/foo.js', 'utf8')).toEqual('formatted:foo()');
expect(fs.readFileSync('/bar.md', 'utf8')).toEqual('formatted:# foo');
Expand Down Expand Up @@ -316,20 +316,20 @@ describe('with git', () => {
});

test('ignore files matching patterns from the repositories root .prettierignore', () => {
const onWriteFile = jest.fn();
const onProcessFile = jest.fn();
mockGitFs('', {
'/.prettierignore': '*.md',
});
prettyQuick('/sub-directory/', { since: 'banana', onWriteFile });
expect(onWriteFile.mock.calls).toEqual([['./foo.js']]);
prettyQuick('/sub-directory/', { since: 'banana', onProcessFile });
expect(onProcessFile.mock.calls).toEqual([['./foo.js']]);
});

test('ignore files matching patterns from the working directories .prettierignore', () => {
const onWriteFile = jest.fn();
const onProcessFile = jest.fn();
mockGitFs('', {
'/sub-directory/.prettierignore': '*.md',
});
prettyQuick('/sub-directory/', { since: 'banana', onWriteFile });
expect(onWriteFile.mock.calls).toEqual([['./foo.js']]);
prettyQuick('/sub-directory/', { since: 'banana', onProcessFile });
expect(onProcessFile.mock.calls).toEqual([['./foo.js']]);
});
});
58 changes: 29 additions & 29 deletions src/__tests__/scm-hg.test.js
Expand Up @@ -106,51 +106,51 @@ describe('with hg', () => {
expect(onFoundChangedFiles).toHaveBeenCalledWith(['./foo.js', './bar.md']);
});

test('calls onWriteFile with changed files', () => {
const onWriteFile = jest.fn();
test('calls onProcessFile with changed files', () => {
const onProcessFile = jest.fn();
mockHgFs();

prettyQuick('root', { since: 'banana', onWriteFile });
prettyQuick('root', { since: 'banana', onProcessFile });

expect(onWriteFile).toHaveBeenCalledWith('./foo.js');
expect(onWriteFile).toHaveBeenCalledWith('./bar.md');
expect(onProcessFile).toHaveBeenCalledWith('./foo.js');
expect(onProcessFile).toHaveBeenCalledWith('./bar.md');
});

test('calls onWriteFile with changed files for the given pattern', () => {
const onWriteFile = jest.fn();
test('calls onProcessFile with changed files for the given pattern', () => {
const onProcessFile = jest.fn();
mockHgFs();
prettyQuick('root', { pattern: '*.md', since: 'banana', onWriteFile });
expect(onWriteFile.mock.calls).toEqual([['./bar.md']]);
prettyQuick('root', { pattern: '*.md', since: 'banana', onProcessFile });
expect(onProcessFile.mock.calls).toEqual([['./bar.md']]);
});

test('calls onWriteFile with changed files for the given globstar pattern', () => {
const onWriteFile = jest.fn();
test('calls onProcessFile with changed files for the given globstar pattern', () => {
const onProcessFile = jest.fn();
mockHgFs();
prettyQuick('root', {
pattern: '**/*.md',
since: 'banana',
onWriteFile,
onProcessFile,
});
expect(onWriteFile.mock.calls).toEqual([['./bar.md']]);
expect(onProcessFile.mock.calls).toEqual([['./bar.md']]);
});

test('calls onWriteFile with changed files for the given extglob pattern', () => {
const onWriteFile = jest.fn();
test('calls onProcessFile with changed files for the given extglob pattern', () => {
const onProcessFile = jest.fn();
mockHgFs();
prettyQuick('root', {
pattern: '*.*(md|foo|bar)',
since: 'banana',
onWriteFile,
onProcessFile,
});
expect(onWriteFile.mock.calls).toEqual([['./bar.md']]);
expect(onProcessFile.mock.calls).toEqual([['./bar.md']]);
});

test('writes formatted files to disk', () => {
const onWriteFile = jest.fn();
const onProcessFile = jest.fn();

mockHgFs();

prettyQuick('root', { since: 'banana', onWriteFile });
prettyQuick('root', { since: 'banana', onProcessFile });

expect(fs.readFileSync('/foo.js', 'utf8')).toEqual('formatted:foo()');
expect(fs.readFileSync('/bar.md', 'utf8')).toEqual('formatted:# foo');
Expand All @@ -172,15 +172,15 @@ describe('with hg', () => {
expect(result).toEqual({ errors: ['BAIL_ON_WRITE'], success: false });
});

test('calls onWriteFile with changed files for an array of globstar patterns', () => {
const onWriteFile = jest.fn();
test('calls onProcessFile with changed files for an array of globstar patterns', () => {
const onProcessFile = jest.fn();
mockHgFs();
prettyQuick('root', {
pattern: ['**/*.foo', '**/*.md', '**/*.bar'],
since: 'banana',
onWriteFile,
onProcessFile,
});
expect(onWriteFile.mock.calls).toEqual([['./bar.md']]);
expect(onProcessFile.mock.calls).toEqual([['./bar.md']]);
});

test('without --staged does NOT stage changed files', () => {
Expand Down Expand Up @@ -215,20 +215,20 @@ describe('with hg', () => {
});

test('ignore files matching patterns from the repositories root .prettierignore', () => {
const onWriteFile = jest.fn();
const onProcessFile = jest.fn();
mockHgFs({
'/.prettierignore': '*.md',
});
prettyQuick('/sub-directory/', { since: 'banana', onWriteFile });
expect(onWriteFile.mock.calls).toEqual([['./foo.js']]);
prettyQuick('/sub-directory/', { since: 'banana', onProcessFile });
expect(onProcessFile.mock.calls).toEqual([['./foo.js']]);
});

test('ignore files matching patterns from the working directories .prettierignore', () => {
const onWriteFile = jest.fn();
const onProcessFile = jest.fn();
mockHgFs({
'/sub-directory/.prettierignore': '*.md',
});
prettyQuick('/sub-directory/', { since: 'banana', onWriteFile });
expect(onWriteFile.mock.calls).toEqual([['./foo.js']]);
prettyQuick('/sub-directory/', { since: 'banana', onProcessFile });
expect(onProcessFile.mock.calls).toEqual([['./foo.js']]);
});
});
27 changes: 0 additions & 27 deletions src/formatFiles.js

This file was deleted.

16 changes: 11 additions & 5 deletions src/index.js
@@ -1,5 +1,5 @@
import scms from './scms';
import formatFiles from './formatFiles';
import processFiles from './processFiles';
import createIgnorer from './createIgnorer';
import createMatcher from './createMatcher';
import isSupportedExtension from './isSupportedExtension';
Expand All @@ -14,11 +14,12 @@ export default (
restage = true,
branch,
bail,
check,
verbose,
onFoundSinceRevision,
onFoundChangedFiles,
onPartiallyStagedFile,
onWriteFile,
onProcessFile,
onExamineFile,
smably marked this conversation as resolved.
Show resolved Hide resolved
} = {}
) => {
Expand Down Expand Up @@ -60,10 +61,15 @@ export default (

const failReasons = new Set();

formatFiles(directory, changedFiles, {
processFiles(directory, changedFiles, {
check,
config,
onWriteFile: file => {
onWriteFile && onWriteFile(file);
onProcessFile: file => {
onProcessFile && onProcessFile(file);
if (check) {
failReasons.add('CHECK_FAILED');
return;
}
if (bail) {
failReasons.add('BAIL_ON_WRITE');
}
Expand Down
35 changes: 35 additions & 0 deletions src/processFiles.js
@@ -0,0 +1,35 @@
import { readFileSync, writeFileSync } from 'fs';
import * as prettier from 'prettier';
import { join } from 'path';

export default (
directory,
files,
{ check, config, onProcessFile, onExamineFile } = {}
smably marked this conversation as resolved.
Show resolved Hide resolved
) => {
for (const relative of files) {
onExamineFile && onExamineFile(relative);
const file = join(directory, relative);
const options = Object.assign(
{},
prettier.resolveConfig.sync(file, {
config,
editorconfig: true,
}),
{ filepath: file }
);
const input = readFileSync(file, 'utf8');

if (check && !prettier.check(input, options)) {
smably marked this conversation as resolved.
Show resolved Hide resolved
onProcessFile && onProcessFile(relative);
return;
}

const output = prettier.format(input, options);

if (output !== input) {
writeFileSync(file, output);
onProcessFile && onProcessFile(relative);
}
}
};