UNPKG

8.23 kBPlain TextView Raw
1#!/usr/bin/env node
2/**
3 * Copyright (c) Facebook, Inc. and its affiliates.
4 *
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9var fs = require('fs');
10var path = require('path');
11
12var flowRemoveTypes = require('./index');
13
14var usage = 'Usage: flow-remove-types [options] [sources] \n' +
15
16'\nOptions:\n' +
17' -h, --help Show this message\n' +
18' -v, --version Prints the current version of flow-remove-types\n' +
19' -i, --ignore Paths to ignore, Regular Expression\n' +
20' -x, --extensions File extensions to transform\n' +
21' -o, --out-file The file path to write transformed file to\n' +
22' -d, --out-dir The directory path to write transformed files within\n' +
23' -a, --all Transform all files, not just those with a @flow comment\n' +
24' -p, --pretty Remove flow types without replacing with spaces, \n' +
25' producing prettier output but may require using source maps\n' +
26' --ignore-uninitialized-fields\n' +
27' Removes uninitialized class fields (`foo;`, `foo: string;`)\n' +
28' completely rather than only removing the type. THIS IS NOT\n' +
29' SPEC COMPLIANT! Instead, use `declare foo: string;` for\n' +
30' type-only fields.\n' +
31' -m, --sourcemaps Also output source map files. Optionally pass "inline"\n' +
32' -q, --quiet Does not produce any output concerning successful progress.\n' +
33
34'\nExamples:\n' +
35
36'\nTransform one file:\n' +
37' flow-remove-types --out-file output.js input.js\n' +
38
39'\nTransform many files:\n' +
40' flow-remove-types --out-dir out/ input1.js input2.js\n' +
41
42'\nTransform files in directory:\n' +
43' flow-remove-types --out-dir out/ indir/\n' +
44
45'\nTransform files with source maps:\n' +
46' flow-remove-types --out-dir out/ indir/ --sourcemaps\n' +
47
48'\nTransform files with inline source maps:\n' +
49' flow-remove-types --out-dir out/ indir/ --sourcemaps inline\n' +
50
51'\nTransform stdin:\n' +
52' cat input.js | flow-remove-types > output.js\n';
53
54var _memo = {};
55
56function mkdirp(dirpath) {
57 if (_memo[dirpath]) {
58 return;
59 }
60 _memo[dirpath] = true;
61 try {
62 fs.mkdirSync(dirpath);
63 } catch (err) {
64 if (err.code === 'ENOENT') {
65 mkdirp(path.dirname(dirpath));
66 fs.mkdirSync(dirpath);
67 } else {
68 try {
69 stat = fs.statSync(dirpath);
70 } catch (ignored) {
71 throw err;
72 }
73 if (!stat.isDirectory()) {
74 throw err;
75 }
76 }
77 }
78}
79
80// Collect arguments
81var ignore = /node_modules/;
82var extensions = [ '.js', '.mjs', '.cjs', '.jsx', '.flow', '.es6' ];
83var outDir;
84var outFile;
85var all;
86var pretty;
87var ignoreUninitializedFields;
88var sourceMaps;
89var inlineSourceMaps;
90var quiet;
91var sources = [];
92var i = 2;
93while (i < process.argv.length) {
94 var arg = process.argv[i++];
95 if (arg === '-h' || arg === '--help') {
96 process.stdout.write(usage);
97 process.exit(0);
98 } else if (arg === '-v' || arg === '--version') {
99 process.stdout.write('v' + require('./package').version);
100 process.exit(0);
101 } else if (arg === '-i' || arg === '--ignore') {
102 ignore = new RegExp(process.argv[i++]);
103 } else if (arg === '-x' || arg === '--extensions') {
104 extensions = process.argv[i++].split(',');
105 } else if (arg === '-o' || arg === '--out-file') {
106 outFile = process.argv[i++];
107 } else if (arg === '-d' || arg === '--out-dir') {
108 outDir = process.argv[i++];
109 } else if (arg === '-a' || arg === '--all') {
110 all = true;
111 } else if (arg === '-p' || arg === '--pretty') {
112 pretty = true;
113 } else if (arg === '--ignore-uninitialized-fields') {
114 ignoreUninitializedFields = true;
115 } else if (arg === '-m' || arg === '--sourcemaps') {
116 sourceMaps = true;
117 if (process.argv[i] === 'inline') {
118 inlineSourceMaps = true;
119 i++;
120 }
121 } else if (arg === '-q' || arg === '--quiet') {
122 quiet = true;
123 } else {
124 sources.push(arg);
125 }
126}
127
128function info(msg) {
129 if (!quiet) {
130 process.stderr.write(msg);
131 }
132}
133
134function error(msg) {
135 process.stderr.write('\n\033[31m ' + msg + '\033[0m\n\n');
136 process.exit(1);
137}
138
139// Validate arguments
140if (outDir && outFile) {
141 error('Only specify one of --out-dir or --out-file');
142}
143
144if (outDir && sources.length === 0) {
145 error('Must specify files when providing --out-dir');
146}
147
148if (!outDir && !outFile && sourceMaps && !inlineSourceMaps) {
149 error('Must specify either an output path or inline source maps');
150}
151
152// Ensure all sources exist
153for (var i = 0; i < sources.length; i++) {
154 try {
155 var stat = fs.lstatSync(sources[i]);
156 if (sources.length > 1 && !stat.isFile()) {
157 error('Source "' + sources[i] + '" is not a file.');
158 }
159 } catch (err) {
160 error('Source "' + sources[i] + '" does not exist.');
161 }
162}
163
164// Process stdin if no sources were provided
165if (sources.length === 0) {
166 var content = '';
167 process.stdin.setEncoding('utf-8');
168 process.stdin.resume();
169 process.stdin.on('data', function (str) { content += str; });
170 process.stdin.on('end', function () {
171 transformAndOutput(content, outFile);
172 });
173 return;
174}
175
176var isDirSource = sources.length === 1 && fs.statSync(sources[0]).isDirectory();
177
178if ((sources.length > 1 || isDirSource) && !outDir) {
179 error('Multiple files require providing --out-dir');
180}
181
182// Process multiple files
183for (var i = 0; i < sources.length; i++) {
184 var source = sources[i];
185 var stat = fs.lstatSync(source);
186 if (stat.isDirectory()) {
187 var files = fs.readdirSync(source);
188 for (var j = 0; j < files.length; j++) {
189 var subSource = path.join(source, files[j]);
190 if (!ignore || !ignore.test(subSource)) {
191 sources.push(subSource);
192 }
193 }
194 } else if (stat.isFile() && extensions.indexOf(path.extname(source)) !== -1) {
195 if (outDir) {
196 outFile = path.join(outDir, isDirSource ? path.relative(sources[0], source) : source);
197 mkdirp(path.dirname(outFile));
198 }
199 var content = fs.readFileSync(source, 'utf8');
200 transformAndOutput(content, outFile, source);
201 }
202}
203
204function transformAndOutput(content, outFile, source) {
205 var fileName = source || '<stdin>';
206 var result = transformSource(content, fileName);
207 var code = result.toString();
208
209 if (sourceMaps) {
210 var map = result.generateMap();
211 delete map.file;
212 map.sources[0] = fileName;
213
214 if (source) {
215 delete map.sourcesContent;
216 if (outFile) {
217 map.sources[0] = path.join(path.relative(path.dirname(outFile), path.dirname(source)), path.basename(source));
218 }
219 } else {
220 map.sourcesContent = [content];
221 }
222
223 code += '\n//# sourceMappingURL=' + (inlineSourceMaps ?
224 'data:application/json;charset=utf-8;base64,' + btoa(JSON.stringify(map)) :
225 path.basename(outFile) + '.map'
226 ) + '\n';
227 }
228
229 if (outFile) {
230 fs.writeFileSync(outFile, code);
231 info(fileName + '\n \u21B3 \033[32m' + outFile + '\033[0m\n');
232 if (sourceMaps && !inlineSourceMaps) {
233 var mapOutFile = outFile + '.map';
234 fs.writeFileSync(mapOutFile, JSON.stringify(map) + '\n');
235 info('\033[2m \u21B3 \033[32m' + mapOutFile + '\033[0m\n');
236 }
237 } else {
238 process.stdout.write(code);
239 }
240}
241
242function btoa(str) {
243 // There are 5.x versions of Node that have `Buffer.from` but don't have the
244 // `Buffer.from(string)` overload, so check for other new methods to be sure.
245 return (Buffer.from && Buffer.alloc && Buffer.allocUnsafe
246 ? Buffer.from(str)
247 : new Buffer(str)
248 ).toString('base64');
249}
250
251function transformSource(content, filepath) {
252 try {
253 return flowRemoveTypes(content, {
254 all: all,
255 pretty: pretty,
256 ignoreUninitializedFields: ignoreUninitializedFields,
257 });
258 } catch (error) {
259 if (error.loc) {
260 var line = error.loc.line - 1;
261 var col = error.loc.column;
262 var text = content.split(/\r\n?|\n|\u2028|\u2029/)[line];
263 process.stderr.write(
264 filepath + '\n' +
265 ' \u21B3 \033[31mSyntax Error: ' + error.message + '\033[0m\n' +
266 ' \033[90m' + line + ': \033[0m' +
267 text.slice(0, col) + '\033[7;31m' + text[col] + '\033[0m' + text.slice(col + 1) + '\n'
268 );
269 process.exit(1);
270 }
271 throw error;
272 }
273}