Worker.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /*
  2. * Copyright (c) 2015-present, Facebook, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. *
  9. */
  10. 'use strict';
  11. const EventEmitter = require('events').EventEmitter;
  12. const async = require('neo-async');
  13. const fs = require('fs');
  14. const writeFileAtomic = require('write-file-atomic');
  15. const getParser = require('./getParser');
  16. const jscodeshift = require('./core');
  17. let emitter;
  18. let finish;
  19. let notify;
  20. let transform;
  21. let parser;
  22. if (module.parent) {
  23. emitter = new EventEmitter();
  24. emitter.send = (data) => { run(data); };
  25. finish = () => { emitter.emit('disconnect'); };
  26. notify = (data) => { emitter.emit('message', data); };
  27. module.exports = (args) => {
  28. setup(args[0], args[1]);
  29. return emitter;
  30. };
  31. } else {
  32. finish = () => setImmediate(() => process.disconnect());
  33. notify = (data) => { process.send(data); };
  34. process.on('message', (data) => { run(data); });
  35. setup(process.argv[2], process.argv[3]);
  36. }
  37. function prepareJscodeshift(options) {
  38. if (parser) {
  39. return jscodeshift.withParser(parser);
  40. } else if (options.parser) {
  41. return jscodeshift.withParser(getParser(options.parser));
  42. } else {
  43. return jscodeshift;
  44. }
  45. }
  46. function setup(tr, babel) {
  47. if (babel === 'babel') {
  48. require('babel-register')({
  49. babelrc: false,
  50. presets: [
  51. require('babel-preset-es2015'),
  52. require('babel-preset-stage-1'),
  53. ],
  54. plugins: [
  55. require('babel-plugin-transform-flow-strip-types'),
  56. ]
  57. });
  58. }
  59. const module = require(tr);
  60. transform = typeof module.default === 'function' ?
  61. module.default :
  62. module;
  63. if (module.parser) {
  64. parser = typeof module.parser === 'string' ?
  65. getParser(module.parser) :
  66. module.parser;
  67. }
  68. }
  69. function free() {
  70. notify({action: 'free'});
  71. }
  72. function updateStatus(status, file, msg) {
  73. msg = msg ? file + ' ' + msg : file;
  74. notify({action: 'status', status: status, msg: msg});
  75. }
  76. function empty() {}
  77. function stats(name, quantity) {
  78. quantity = typeof quantity !== 'number' ? 1 : quantity;
  79. notify({action: 'update', name: name, quantity: quantity});
  80. }
  81. function trimStackTrace(trace) {
  82. if (!trace) {
  83. return '';
  84. }
  85. // Remove this file from the stack trace of an error thrown in the transformer
  86. const lines = trace.split('\n');
  87. const result = [];
  88. lines.every(function(line) {
  89. if (line.indexOf(__filename) === -1) {
  90. result.push(line);
  91. return true;
  92. }
  93. });
  94. return result.join('\n');
  95. }
  96. function run(data) {
  97. const files = data.files;
  98. const options = data.options || {};
  99. if (!files.length) {
  100. finish();
  101. return;
  102. }
  103. async.each(
  104. files,
  105. function(file, callback) {
  106. fs.readFile(file, function(err, source) {
  107. if (err) {
  108. updateStatus('error', file, 'File error: ' + err);
  109. callback();
  110. return;
  111. }
  112. source = source.toString();
  113. try {
  114. const jscodeshift = prepareJscodeshift(options);
  115. const out = transform(
  116. {
  117. path: file,
  118. source: source,
  119. },
  120. {
  121. j: jscodeshift,
  122. jscodeshift: jscodeshift,
  123. stats: options.dry ? stats : empty
  124. },
  125. options
  126. );
  127. if (!out || out === source) {
  128. updateStatus(out ? 'nochange' : 'skip', file);
  129. callback();
  130. return;
  131. }
  132. if (options.print) {
  133. console.log(out); // eslint-disable-line no-console
  134. }
  135. if (!options.dry) {
  136. writeFileAtomic(file, out, function(err) {
  137. if (err) {
  138. updateStatus('error', file, 'File writer error: ' + err);
  139. } else {
  140. updateStatus('ok', file);
  141. }
  142. callback();
  143. });
  144. } else {
  145. updateStatus('ok', file);
  146. callback();
  147. }
  148. } catch(err) {
  149. updateStatus(
  150. 'error',
  151. file,
  152. 'Transformation error\n' + trimStackTrace(err.stack)
  153. );
  154. callback();
  155. }
  156. });
  157. },
  158. function(err) {
  159. if (err) {
  160. updateStatus('error', '', 'This should never be shown!');
  161. }
  162. free();
  163. }
  164. );
  165. }