18

I have lots of HTML files in a directory and sub-directories. I can execute js-beautify command through the command line and want to apply it recursively to all those files.

I have tried

find . -name ".html" -type f | js-beautify -randjs-beautify -r | find . -name ".html" -type f

but it doesn't work. However, JS-beautify does work if I give something like js-beautify -r myfile.html or js-beautify -r *.html (in case of all the files in a directory but not in sub-directory)

Can anyone tell me how should I be piping these two commands?

Pervy Sage
  • 841
  • 1
  • 10
  • 22

6 Answers6

23

However, JS-beautify does work ... in case of all the files in a directory but not in sub-directory

You've mentioned that JS-beautify works if all the input files are in the same directory. Your command doesn't probably work because you pass all the results from find which might include input files from different directories.

As mentioned in the comment earlier, you could use -exec instead:

find . -type f -name "*.html" -exec js-beautify -r {} \;

Newer versions of GNU find might use this syntax:

find . -type f -name "*.html" -exec js-beautify -r {} +
Cees Timmerman
  • 17,623
  • 11
  • 91
  • 124
devnull
  • 118,548
  • 33
  • 236
  • 227
  • 2
    In case anyone is doing this on a Mac, and the -r command doesn't work, I've managed to get this working instead: `find . -type f -name "*.js" -exec sh -c 'js-beautify {} >> {}' \;` – Tiago Feb 17 '15 at 14:31
  • 1
    Or `find . -type f -name "*.js" -exec sh -c 'js-beautify {} > {}2;mv {}2 {}' \;` if it doesn't work fully. Maybe someone has a better/more efficient way of doing this in a line. – Tiago Feb 17 '15 at 14:58
  • Probably this answer was working back in the time but currently it looks like js-beautify executing wrong beautifier on html files (probably js) if used within this command – Andrey Feb 12 '18 at 17:36
  • 1
    @Tiago The second version worked for me, thanks! The first one only appends the "beautified" content to the files, resulting in duplicate content. – frzsombor Apr 25 '19 at 10:00
16

I've run into a similar problem and found a simple cross-platform solution using glob-run:

npm i -g glob-run js-beautify
glob-run html-beautify -r **/*.html

It would be nice if js-beautify supported globs itself, though.

1j01
  • 3,714
  • 2
  • 29
  • 30
  • Doesn't put quotes around or escape the dynamic path in `cmd.exe` or Git Bash. – Cees Timmerman May 15 '19 at 14:12
  • @CeesTimmerman I only did this as a one-off operation, so I don't know how well it really works. Feel free to improve this answer, point out a pitfall example or show how to do escaping properly. I see on the glob-run docs it suggests escaping like `\*` - does that work (cross-platform)? – 1j01 May 16 '19 at 21:18
  • I've already filed [an issue](https://github.com/fahad19/glob-run/issues/7). `js-beautify -r "blocks/block-search-results (class selection).html"` should work. – Cees Timmerman May 17 '19 at 09:14
7

find+xargs is the way to go. It is faster than find with -exec.

find . -name '*.html' | xargs js-beautify 

If for some reason, you have spaces in your filenames, you'll want to do it like this...

find . -name '*.html' -print0 | xargs -0 js-beautify

Finally, if for some strange reason, js-beautify won't work with multiple arguments, then you'll need to tell xargs to only pass in one argument at a time. This isn't much different than using the -exec option, but it's better IMO because it's just more consistent.

find . -name '*.html' | xargs -n 1 js-beautify

Note, you can combine the -print0 and xargs -0 options with xargs -n 1.

edit: As pointed out by T.J. Crowder, the shell will not glob wildcards in double-quotes. This was news to me, perhaps there is some ancient environment out there where that isn't true, and hopefully, you'll never be forced to work in it.

BillRobertson42
  • 12,602
  • 4
  • 40
  • 57
  • The shell will glob them if they're in double quotes? I've never run into that. (Which is to say: I've never missed out files in subdirectories matching the spec because the shell globbed only files in the current directory when I ran the command.) – T.J. Crowder Apr 23 '14 at 21:18
  • If you downvote other answers on posts that you've answered, might as well consider leaving a note as to what could be improved in the answer. – devnull Apr 27 '14 at 03:52
  • @devnull I already stated why using the -exec option is not a good choice. In addition to the speed issue, which admittedly may not matter for everybody's problem size, the syntax for the -exec option is harder to explain. e.g. the \; Finally, knowing how xargs works is very useful in other situations. e.g. when you have a file of filenames you wish to execute a command on. That is how I feel on the matter. Despite all of this, you are correct. I should have left a comment instead. SO won't let me change the answer. – BillRobertson42 Apr 27 '14 at 13:02
  • @T.J.Crowder I was taught this by old AT&T Unix guys. It may have been true back in the day, however, I can't reproduce that on any shell that I have access to at the moment. I will amend the answer. – BillRobertson42 Apr 27 '14 at 13:04
  • @Bill It's ok. Your downvote is justified. I can understand what you might have gone through. You can find more of my answers [here](http://stackoverflow.com/users/2235132/devnull?tab=answers). Good luck! – devnull Apr 27 '14 at 13:05
  • @devnull Sorry, meant to say, SO won't let me change the downvote unless the answer has been edited. – BillRobertson42 Apr 27 '14 at 13:11
  • Why does this answer not have the `-r` option? – Merchako Jun 03 '16 at 15:23
  • Alas, `js-beautify` won't take multiple files as arguments as of 1.6.2. – Merchako Jun 03 '16 at 20:00
6

1) Add these dependencies to your project

npm install --save-dev glob js-beautify

2) Create scripts/format.js

const jsBeautify = require('js-beautify')['js_beautify'];
const fs = require('fs');
const glob = require('glob');

const options = {
  indent_size: 2,
  indent_char: ' ',
  indent_with_tabs: false,
  eol: '\n',
  end_with_newline: true,
  indent_level: 0,
  preserve_newlines: true,
  max_preserve_newlines: 10,
  space_in_paren: false,
  space_in_empty_paren: false,
  jslint_happy: false,
  space_after_anon_function: false,
  brace_style: 'collapse',
  break_chained_methods: false,
  keep_array_indentation: false,
  unescape_strings: false,
  wrap_line_length: 0,
  e4x: false,
  comma_first: false,
  operator_position: 'before-newline'
};

glob('**/!(node_modules)/**/*.js', { absolute: true }, (er, files) => {
  files.forEach(file => {
    console.log(`js-beautify ${file}`);
    const data = fs.readFileSync(file, 'utf8');
    const nextData = jsBeautify(data, options);
    fs.writeFileSync(file, nextData, 'utf8');
  });
});

3) Add a format script to package.json

"scripts": {
  "format": "node ./scripts/format.js"
}

4) In your project, run

npm run format
Jamie Mason
  • 4,159
  • 2
  • 32
  • 42
1

It seems like file globs are supported as of v1.8.9 (released December 2018). For example:

js-beautify --html --replace '**/*.html'
Sean Leather
  • 1,182
  • 1
  • 9
  • 25
0

Combining Bill's wisdom above and these answers on regexp matching, this is the actual solution for my project:

find . -regextype egrep -regex './(src|test|app)/.*.(js|sass|html)' -print0 | xargs -0 ./node_modules/.bin/js-beautify -r

  • only looks in the right folders (i.e. not node_modules)
  • goes after js, sass, html
  • can handle file names with spaces
  • rewrites in place (-r)
  • does not rely on the node shebang
  • works with a locally installed js-beautify (./node_modules/.bin)

When used inside a package.json script, ./node_modules/.bin is automatically in the path, \.* needs to be escaped to \\.*, thus:

"beautify2": "find . -regextype egrep -regex './(src|test|app)/.*\\.(js|sass|html)' -print0 | xargs -0 js-beautify -r"

beautified app/index.html
beautified app/sass/_atomic.sass
beautified app/sass/_mixin.sass
beautified app/sass/space test.sass
beautified test/Log.test.js
beautified test/deleteAction.test.js
beautified src/util/fileUtils.js
...
Frank N
  • 9,625
  • 4
  • 80
  • 110
  • I had to add type to your command otherwise it errors once it hits the first folder: find . -type f -regextype egrep -regex './(src|test|app)/.*.(js|sass|html)' -print0 | xargs -0 ./node_modules/.bin/js-beautify -r – Will Bowman Dec 17 '19 at 17:00