1 | #!/usr/bin/env node
|
2 | var fs = require('fs');
|
3 | var util = require('util');
|
4 | var path = require('path');
|
5 | var child_process = require('child_process');
|
6 |
|
7 | var esformatter = require('esformatter');
|
8 | var escodegen = require('escodegen');
|
9 | var _ = require('underscore');
|
10 |
|
11 | var jsfmt = require('../index.js');
|
12 |
|
13 | var tmp = require('tmp');
|
14 | tmp.setGracefulCleanup();
|
15 |
|
16 | var argv = require('minimist')(process.argv.slice(2), {
|
17 | 'string': ['rewrite', 'search'],
|
18 | 'boolean': ['comments', 'diff', 'format', 'list', 'write'],
|
19 | 'default': {
|
20 | comments: true,
|
21 | diff: false,
|
22 | format: false,
|
23 | list: false,
|
24 | write: false
|
25 | },
|
26 | 'alias': {
|
27 | comments: 'c',
|
28 | diff: 'd',
|
29 | format: 'f',
|
30 | list: 'l',
|
31 | rewrite: 'r',
|
32 | search: 's',
|
33 | write: 'w'
|
34 | }
|
35 | });
|
36 |
|
37 | if (argv.help || (!argv.format && !argv.search && !argv.rewrite)) {
|
38 | console.log('jsfmt [flags] [path ...]');
|
39 | console.log('\tAction:');
|
40 | console.log('\t--format=false, -f=false: format the input javascript');
|
41 | console.log('\t--search="", -s="": search rule (e.g., \'a.slice\')');
|
42 | console.log('\t--rewrite="", -r="": rewrite rule (e.g., \'a.slice(b, len(a) -> a.slice(b)\')');
|
43 | console.log('');
|
44 | console.log('\tOutput (default is stdout):');
|
45 | console.log("\t--list=false, -l=false: list files whose formatting differs from jsfmt's");
|
46 | console.log('\t--diff=false, -d=false: display diffs instead of rewriting files');
|
47 | console.log('\t--write=false, -w=false: write result to (source) file instead of stdout');
|
48 | console.log('');
|
49 | console.log('\tConfig:');
|
50 | console.log('\t--comments=true, -c=true: include comments in result');
|
51 | return;
|
52 | }
|
53 |
|
54 | function diff(pathA, pathB, callback) {
|
55 | child_process.exec([
|
56 | 'git', 'diff', '--ignore-space-at-eol', '-b', '--no-index', '--', pathA, pathB
|
57 | ].join(' '), callback);
|
58 | }
|
59 |
|
60 | function handleDiff(fullPath, originalJavascript, formattedJavascript) {
|
61 | if (fullPath == 'stdin') {
|
62 | tmp.file(function(err, pathA, fdA) {
|
63 | if (err) throw err;
|
64 | fs.writeSync(fdA, originalJavascript);
|
65 |
|
66 | tmp.file(function(err, pathB, fdB) {
|
67 | if (err) throw err;
|
68 | fs.writeSync(fdB, formattedJavascript);
|
69 |
|
70 | diff(pathA, pathB, function(err, stdout, stderr) {
|
71 | if (stdout) console.log(stdout);
|
72 | if (stderr) console.log(stderr);
|
73 | });
|
74 | });
|
75 | });
|
76 | } else {
|
77 | tmp.file(function(err, pathA, fdA) {
|
78 | if (err) throw err;
|
79 | fs.writeSync(fdA, formattedJavascript);
|
80 |
|
81 | diff(fullPath, pathA, function(err, stdout, stderr) {
|
82 | if (stdout) console.log(stdout);
|
83 | if (stderr) console.log(stderr);
|
84 | });
|
85 | });
|
86 | }
|
87 | }
|
88 |
|
89 | function handleJavascript(fullPath, original) {
|
90 | var formattingOptions = {
|
91 | preset: 'default',
|
92 | indent: {
|
93 | value: ' '
|
94 | }
|
95 | };
|
96 |
|
97 | var js = original;
|
98 |
|
99 | if (argv.search) {
|
100 | var cwd = process.cwd();
|
101 | var relativePath = path.relative(cwd, fullPath);
|
102 | jsfmt.search(js, argv.search).forEach(function(match) {
|
103 | var node = match.node;
|
104 | var loc = node.loc;
|
105 | var startLine = loc.start.line;
|
106 | var endLine = loc.end.line;
|
107 | console.log([relativePath, _.uniq([startLine, endLine]).join(':')].join(':'));
|
108 |
|
109 | var partialJavascript = js.split('\n').slice(startLine - 1, endLine).join('\n');
|
110 | console.log(partialJavascript, '\n');
|
111 | });
|
112 | return;
|
113 | }
|
114 |
|
115 | if (argv.rewrite) {
|
116 | js = jsfmt.rewrite(js, argv.rewrite).toString();
|
117 | }
|
118 |
|
119 | if (argv.format) {
|
120 | js = esformatter.format(js, formattingOptions);
|
121 | }
|
122 |
|
123 | if (argv.diff) {
|
124 | handleDiff(fullPath, original, js)
|
125 | } else if (argv.list && original != js) {
|
126 | // Print filenames who differ
|
127 | console.log(fullPath);
|
128 | } else if (argv.write) {
|
129 | // Overwrite original file
|
130 | fs.writeFileSync(fullPath, js);
|
131 | } else {
|
132 | // Print to stdout
|
133 | console.log(js);
|
134 | }
|
135 | }
|
136 |
|
137 | function handleDirectory(currentPath, callback) {
|
138 | child_process.execFile('find', [currentPath, '-name', '*.js'], function(err, stdout, stderr) {
|
139 | var paths = _.filter(stdout.split('\n').slice(0, -1), function(currentPath) {
|
140 | return path.basename(currentPath).indexOf('.') != 0; // Remove hidden files
|
141 | });
|
142 | callback(paths);
|
143 | });
|
144 | }
|
145 |
|
146 | var paths = argv._;
|
147 |
|
148 | if (paths.length > 0) {
|
149 | paths.forEach(function(currentPath) {
|
150 | var fullPath = path.join(process.cwd(), currentPath);
|
151 | if (fs.statSync(fullPath).isDirectory()) {
|
152 | handleDirectory(fullPath, function(paths) {
|
153 | _.each(paths, function(fullPath) {
|
154 | handleJavascript(path.normalize(fullPath), fs.readFileSync(fullPath, 'utf-8'));
|
155 | });
|
156 | });
|
157 | } else {
|
158 | handleJavascript(fullPath, fs.readFileSync(fullPath, 'utf-8'));
|
159 | }
|
160 | });
|
161 | } else {
|
162 | var js = '';
|
163 | process.stdin.setEncoding('utf8');
|
164 | process.stdin.on('readable', function(chunk) {
|
165 | var chunk = process.stdin.read();
|
166 | if (chunk != null) {
|
167 | js += chunk;
|
168 | }
|
169 | });
|
170 | process.stdin.on('end', function() {
|
171 | handleJavascript('stdin', js);
|
172 | });
|
173 | }
|