Skip to content

Commit

Permalink
feat: replace occurrences of {} in formatter command with filepath
Browse files Browse the repository at this point in the history
  • Loading branch information
hallettj committed Jul 27, 2018
1 parent 84ab8de commit e986b4d
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 5 deletions.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ For detailed information run:
The command expects a shell command to run a formatter, and one or more file
patterns to identify which files should be formatted. For example:

$ git-format-staged --formatter 'prettier --stdin' 'src/*.js'
$ git-format-staged --formatter 'prettier --stdin --stdin-filepath "{}"' 'src/*.js'

That will format all files under `src/` and its subdirectories using
`prettier`. The file pattern is tested against staged files using Python's
Expand All @@ -84,6 +84,16 @@ git-format-staged never operates on files that are excluded from version
control. So it is not necessary to explicitly exclude stuff like
`node_modules/`.

The formatter command may include a placeholder, `{}`, which will be replaced
with the path of the file that is being formatted. This is useful if your
formatter needs to know the file extension to determine how to format or to
lint each file. For example:

$ git-format-staged -f 'prettier --stdin --stdin-filepath "{}"' '*.js' '*.css'

Do not attempt to read or write to `{}` in your formatter command! The
placeholder exists only for referencing the file name and path.

### Check staged changes with a linter without formatting

Perhaps you do not want to reformat files automatically; but you do want to
Expand Down
9 changes: 5 additions & 4 deletions git-format-staged
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def format_staged_files(file_patterns, formatter, git_root, update_working_tree=
# Returns hash of the new object if formatting produced any changes.
def format_file_in_index(formatter, diff_entry, update_working_tree=True, write=True):
orig_hash = diff_entry['dst_hash']
new_hash = format_object(formatter, orig_hash)
new_hash = format_object(formatter, orig_hash, diff_entry['src_path'])

# If the new hash is the same then the formatter did not make any changes.
if not write or new_hash == orig_hash:
Expand All @@ -77,16 +77,17 @@ def format_file_in_index(formatter, diff_entry, update_working_tree=True, write=

return new_hash

file_path_placeholder = re.compile('\{\}')

# Run formatter on a git blob identified by its hash. Writes output to a new git
# blob, and returns the hash of the new blob.
def format_object(formatter, object_hash):
def format_object(formatter, object_hash, file_path):
get_content = subprocess.Popen(
['git', 'cat-file', '-p', object_hash],
stdout=subprocess.PIPE
)
format_content = subprocess.Popen(
formatter,
re.sub(file_path_placeholder, file_path, formatter),
shell=True,
stdin=get_content.stdout,
stdout=subprocess.PIPE
Expand Down Expand Up @@ -214,7 +215,7 @@ if __name__ == '__main__':
parser.add_argument(
'--formatter', '-f',
required=True,
help='Shell command to format files, will run once per file. (Example: "prettier --stdin")'
help='Shell command to format files, will run once per file. Occurrences of the placeholder `{}` will be replaced with a path to the file being formatted. (Example: "prettier --stdin --stdin-filepath \'{}\'")'
)
parser.add_argument(
'--no-update-working-tree',
Expand Down
29 changes: 29 additions & 0 deletions test/git-format-staged_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,35 @@ test('messages from formatter command can be redirected to stderr', async t => {
t.regex(stderr, /Parsing error: Unexpected token/)
})

test('replaces placeholder in the formatter command with name of file to be formatted', async t => {
const r = repo(t)
await setContent(r, 'index.js', '')
await stage(r, 'index.js')
await formatStaged(r, '--formatter "echo {}" "*.js"')
contentIs(t, await getStagedContent(r, 'index.js'), 'index.js')
})

test('replaces multiple filename placeholders', async t => {
const r = repo(t)
await setContent(r, 'index.js', '')
await stage(r, 'index.js')
await formatStaged(r, '--formatter "echo {} {}" "*.js"')
contentIs(t, await getStagedContent(r, 'index.js'), 'index.js index.js')
})

test('replaces filename placeholders with relative path to files in subdirectories', async t => {
const r = repo(t)
await fileInTree(r, 'test/testIndex.js', 'function test () {}')
await setContent(r, 'test/testIndex.js', '')
await stage(r, 'test/testIndex.js')
await formatStaged(r, '--formatter "echo {}" "*.js"')
contentIs(
t,
await getStagedContent(r, 'test/testIndex.js'),
'test/testIndex.js'
)
})

function contentIs (t: ExecutionContext<>, actual: string, expected: string) {
t.is(trim(actual), trim(expected))
}
Expand Down

0 comments on commit e986b4d

Please sign in to comment.