import { Transform, TransformOptions, Readable } from "stream"; import { CSVParseParam, mergeParams } from "./Parameters"; import { ParseRuntime, initParseRuntime } from "./ParseRuntime"; import P from "bluebird"; import { stringToLines } from "./fileline"; import { map } from "lodash/map"; import { RowSplit, RowSplitResult } from "./rowSplit"; import getEol from "./getEol"; import lineToJson, { JSONResult } from "./lineToJson"; import { Processor, ProcessLineResult } from "./Processor"; // import { ProcessorFork } from "./ProcessFork"; import { ProcessorLocal } from "./ProcessorLocal"; import { Result } from "./Result"; import CSVError from "./CSVError"; import { bufFromString } from "./util"; export class Converter extends Transform implements PromiseLike { preRawData(onRawData: PreRawDataCallback): Converter { this.runtime.preRawDataHook = onRawData; return this; } preFileLine(onFileLine: PreFileLineCallback): Converter { this.runtime.preFileLineHook = onFileLine; return this; } subscribe( onNext?: (data: any, lineNumber: number) => void | PromiseLike, onError?: (err: CSVError) => void, onCompleted?: () => void): Converter { this.parseRuntime.subscribe = { onNext, onError, onCompleted } return this; } fromFile(filePath: string, options?: string | CreateReadStreamOption | undefined): Converter { const fs = require("fs"); // var rs = null; // this.wrapCallback(cb, function () { // if (rs && rs.destroy) { // rs.destroy(); // } // }); fs.exists(filePath, (exist) => { if (exist) { const rs = fs.createReadStream(filePath, options); rs.pipe(this); } else { this.emit('error', new Error("File does not exist. Check to make sure the file path to your csv is correct.")); } }); return this; } fromStream(readStream: Readable): Converter { readStream.pipe(this); return this; } fromString(csvString: string): Converter { const csv = csvString.toString(); const read = new Readable(); let idx = 0; read._read = function (size) { if (idx >= csvString.length) { this.push(null); } else { const str = csvString.substr(idx, size); this.push(str); idx += size; } } return this.fromStream(read); } then(onfulfilled?: (value: any[]) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike): PromiseLike { return new P((resolve, reject) => { this.parseRuntime.then = { onfulfilled: (value: any[]) => { if (onfulfilled) { resolve(onfulfilled(value)); } else { resolve(value as any); } }, onrejected: (err: Error) => { if (onrejected) { resolve(onrejected(err)); } else { reject(err); } } } }); } public get parseParam(): CSVParseParam { return this.params; } public get parseRuntime(): ParseRuntime { return this.runtime; } private params: CSVParseParam; private runtime: ParseRuntime; private processor: Processor; private result: Result; constructor(param?: Partial, public options: TransformOptions = {}) { super(options); this.params = mergeParams(param); this.runtime = initParseRuntime(this); this.result = new Result(this); // if (this.params.fork) { // this.processor = new ProcessorFork(this); // } else { this.processor = new ProcessorLocal(this); // } this.once("error", (err: any) => { // console.log("BBB"); //wait for next cycle to emit the errors. setImmediate(() => { this.result.processError(err); this.emit("done", err); }); }); this.once("done", () => { this.processor.destroy(); }) return this; } _transform(chunk: any, encoding: string, cb: Function) { this.processor.process(chunk) .then((result) => { // console.log(result); if (result.length > 0) { this.runtime.started = true; return this.result.processResult(result); } }) .then(() => { this.emit("drained"); cb(); }, (error) => { this.runtime.hasError = true; this.runtime.error = error; this.emit("error", error); cb(); }); } _flush(cb: Function) { this.processor.flush() .then((data) => { if (data.length > 0) { return this.result.processResult(data); } }) .then(() => { this.processEnd(cb); }, (err) => { this.emit("error", err); cb(); }) } private processEnd(cb) { this.result.endProcess(); this.emit("done"); cb(); } get parsedLineNumber(): number { return this.runtime.parsedLineNumber; } } export interface CreateReadStreamOption { flags?: string; encoding?: string; fd?: number; mode?: number; autoClose?: boolean; start?: number; end?: number; highWaterMark?: number; } export type CallBack = (err: Error, data: Array) => void; export type PreFileLineCallback = (line: string, lineNumber: number) => string | PromiseLike; export type PreRawDataCallback = (csvString: string) => string | PromiseLike;