index.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. "use strict";
  2. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
  3. if (k2 === undefined) k2 = k;
  4. Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
  5. }) : (function(o, m, k, k2) {
  6. if (k2 === undefined) k2 = k;
  7. o[k2] = m[k];
  8. }));
  9. var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
  10. Object.defineProperty(o, "default", { enumerable: true, value: v });
  11. }) : function(o, v) {
  12. o["default"] = v;
  13. });
  14. var __importStar = (this && this.__importStar) || function (mod) {
  15. if (mod && mod.__esModule) return mod;
  16. var result = {};
  17. if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
  18. __setModuleDefault(result, mod);
  19. return result;
  20. };
  21. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  22. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  23. return new (P || (P = Promise))(function (resolve, reject) {
  24. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  25. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  26. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  27. step((generator = generator.apply(thisArg, _arguments || [])).next());
  28. });
  29. };
  30. var __importDefault = (this && this.__importDefault) || function (mod) {
  31. return (mod && mod.__esModule) ? mod : { "default": mod };
  32. };
  33. Object.defineProperty(exports, "__esModule", { value: true });
  34. exports.scheduleAsyncBwaMem = exports.makeReference = exports.writeSequence = exports.asyncBwaMem = void 0;
  35. const child_process_1 = require("child_process");
  36. const os_1 = require("os");
  37. const fs_1 = __importDefault(require("fs"));
  38. const path_1 = __importDefault(require("path"));
  39. const fastq = __importStar(require("fastq"));
  40. const async_exec = (prog, args, onData) => {
  41. return new Promise((resolve, reject) => {
  42. const child = (0, child_process_1.spawn)(prog, args, { shell: true });
  43. child.stdout.on('data', data => onData(data.toString().trim()));
  44. child.stderr.on('data', data => onData(data.toString().trim()));
  45. child.on('error', err => reject(err));
  46. child.on('exit', code => resolve(code));
  47. });
  48. };
  49. const invReplace = (regex, string, by = '_') => string.split('').map(letter => letter.match(regex) ? letter : by).join('');
  50. const writeSequence = (sequenceName, sequence, filePath, lineN = 80) => __awaiter(void 0, void 0, void 0, function* () {
  51. return new Promise((resolve, reject) => __awaiter(void 0, void 0, void 0, function* () {
  52. var _a;
  53. try {
  54. const r = new RegExp(".{1," + lineN + "}", "g");
  55. const regex_sam_restriction = /[>0-9A-Za-z!#$%&+\./:;?@^_|~-]|[\n\t]/g;
  56. const nSeqName = invReplace(regex_sam_restriction, sequenceName);
  57. yield fs_1.default.promises.writeFile(filePath, '>' + nSeqName + '\n' + ((_a = sequence.match(r)) === null || _a === void 0 ? void 0 : _a.join('\n').toUpperCase()));
  58. resolve(true);
  59. }
  60. catch (error) {
  61. console.log(error);
  62. reject(false);
  63. }
  64. }));
  65. });
  66. exports.writeSequence = writeSequence;
  67. const makeReference = (sequenceName, sequence, filePath, lineN = 80) => __awaiter(void 0, void 0, void 0, function* () {
  68. if (yield writeSequence(sequenceName, sequence, filePath, lineN))
  69. yield async_exec('bwa', ['index', filePath], () => console.log);
  70. });
  71. exports.makeReference = makeReference;
  72. const asyncBwaMem = (refPath, reads,
  73. // R1 : string | Array<string>,
  74. // R2 : string | Array<string>,
  75. runName, libName, outputDir, onData, options) => {
  76. return new Promise((resolve, reject) => __awaiter(void 0, void 0, void 0, function* () {
  77. try {
  78. const defaultOptions = {
  79. output_discordant: true,
  80. output_splitted: true,
  81. output_unmapped: true
  82. };
  83. if (typeof options === 'undefined') {
  84. options = defaultOptions;
  85. }
  86. else {
  87. options = Object.assign(Object.assign({}, defaultOptions), options);
  88. }
  89. const refName = path_1.default.parse(refPath).name;
  90. const bwa = 'bwa';
  91. const samblaster = 'samblaster';
  92. const samtools = 'samtools';
  93. const sambamba = 'sambamba';
  94. let readsIn;
  95. let isPairedEnd = false;
  96. if (Array.isArray(reads)) {
  97. isPairedEnd = true;
  98. console.log('Assuming paired end reads');
  99. const [R1, R2] = reads;
  100. const R1_arr = Array.isArray(R1) ? R1.join(' ') : R1;
  101. const R2_arr = Array.isArray(R2) ? R2.join(' ') : R2;
  102. const R1_kitty = R1_arr.slice(-2) === 'gz' ? 'zcat' : 'cat';
  103. const R2_kitty = R2_arr.slice(-2) === 'gz' ? 'zcat' : 'cat';
  104. const R1_in = `'< ${R1_kitty} ${R1_arr}'`;
  105. const R2_in = `'< ${R2_kitty} ${R2_arr}'`;
  106. readsIn = R1_in + ' ' + R2_in;
  107. }
  108. else {
  109. readsIn = reads;
  110. }
  111. let bam = path_1.default.join(outputDir, `bwa_mem_properly_on_${refName}.bam`);
  112. let bamSorted = path_1.default.join(outputDir, `bwa_mem_properly_on_${refName}.sorted.bam`);
  113. let retObj = { bamSorted };
  114. if (options === null || options === void 0 ? void 0 : options.remove_mapped) {
  115. bam = '/dev/null';
  116. delete retObj.bamSorted;
  117. }
  118. const threads = String((0, os_1.cpus)().length);
  119. let samblasterCmd = [];
  120. // https://github.com/GregoryFaust/samblaster
  121. samblasterCmd = ['|', samblaster,
  122. '--addMateTags',
  123. '-a',
  124. '-e', // Exclude reads marked as duplicates from discordant, splitter, and/or unmapped
  125. ];
  126. if ((options === null || options === void 0 ? void 0 : options.output_discordant) || (options === null || options === void 0 ? void 0 : options.output_splitted)) {
  127. console.log('Using samblaster');
  128. if (options === null || options === void 0 ? void 0 : options.output_discordant) {
  129. if (!isPairedEnd) {
  130. console.log('Discordant reads can be found only in paired reads, skipping');
  131. }
  132. else {
  133. const discordantFile = path_1.default.join(outputDir, `bwa_mem_discordants_on_${refName}.sam`);
  134. console.log('Discordant reads file path: ', discordantFile);
  135. samblasterCmd = [...samblasterCmd, '-d', discordantFile];
  136. retObj = Object.assign(Object.assign({}, retObj), { discordantFile });
  137. }
  138. }
  139. if (!isPairedEnd) {
  140. samblasterCmd = [...samblasterCmd, '--ignoreUnmated'];
  141. }
  142. if (options === null || options === void 0 ? void 0 : options.output_splitted) {
  143. const splitterFile = path_1.default.join(outputDir, `bwa_mem_splitters_on_${refName}.sam`);
  144. console.log('Splitted reads file path: ', splitterFile);
  145. samblasterCmd = [...samblasterCmd, '-s', splitterFile];
  146. retObj = Object.assign(Object.assign({}, retObj), { splitterFile });
  147. }
  148. }
  149. if (options === null || options === void 0 ? void 0 : options.output_unmapped) {
  150. const unmappedFile = path_1.default.join(outputDir, `bwa_mem_unmapped_on_${refName}.fq`);
  151. console.log('Unmapped reads file path: ', unmappedFile);
  152. samblasterCmd = [...samblasterCmd, '-u', unmappedFile];
  153. retObj = Object.assign(Object.assign({}, retObj), { unmappedFile });
  154. }
  155. if (!fs_1.default.existsSync(refPath + '.amb')) {
  156. yield async_exec(bwa, ['index', refPath], (message) => onData('[BWA-INDEX] ' + message));
  157. }
  158. console.log(options, samblasterCmd);
  159. const code = yield async_exec(bwa, ['mem',
  160. '-t', threads,
  161. '-R', `"@RG\\tPL:Illumina\\tID:${+(new Date)}\\tSM:${runName}\\tLB:${libName}"`,
  162. refPath,
  163. readsIn,
  164. ...samblasterCmd,
  165. '|',
  166. samtools,
  167. 'view',
  168. '-Sb',
  169. '-',
  170. '>',
  171. bam
  172. ], (message) => onData('[BWA-MEM] ' + message));
  173. onData('[BWA-MEM][EXIT CODE] ' + code);
  174. if (retObj.bamSorted) {
  175. const code_sort = yield async_exec(sambamba, ['sort',
  176. '-t', threads,
  177. bam
  178. ], (message) => onData('[SAMBAMBA-SORT] ' + message));
  179. onData('[SAMBAMBA-SORT][EXIT CODE] ' + code_sort);
  180. fs_1.default.unlinkSync(bam);
  181. }
  182. resolve(retObj);
  183. }
  184. catch (err) {
  185. reject(err);
  186. }
  187. }));
  188. };
  189. exports.asyncBwaMem = asyncBwaMem;
  190. const asyncBwaMemWorker = (args) => __awaiter(void 0, void 0, void 0, function* () {
  191. const { refPath, reads, runName, libName, outputDir, onData } = args;
  192. yield asyncBwaMem(refPath, reads, runName, libName, outputDir, onData);
  193. });
  194. const scheduleAsyncBwaMem = (allReads, refPath, runName, libName, allOutputDir, onData) => __awaiter(void 0, void 0, void 0, function* () {
  195. const q = fastq.promise(asyncBwaMemWorker, 1);
  196. yield Promise.all(allReads.map((reads, i) => q.push({ refPath, reads, runName, libName, outputDir: allOutputDir[i], onData })));
  197. });
  198. exports.scheduleAsyncBwaMem = scheduleAsyncBwaMem;