UNPKG

datainout

Version:

Import and reports data

816 lines (799 loc) 26.2 kB
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) { if (typeof require !== "undefined") return require.apply(this, arguments); throw Error('Dynamic require of "' + x + '" is not supported'); }); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // src/helpers/datainout-config.ts import * as path from "path"; import * as fs from "fs"; var globalConfig = {}; function getConfig() { if (Object.keys(globalConfig).length === 0) { const jsPath = path.join(process.cwd(), "datainout.config.js"); const tsPath = path.join(process.cwd(), "datainout.config.ts"); if (fs.existsSync(jsPath)) globalConfig = __require(jsPath); else if (fs.existsSync(tsPath)) globalConfig = __require(tsPath).default; else globalConfig = { templateExtension: ".js", dateFormat: "DD-MM-YYYY" }; } return globalConfig; } // src/helpers/path-file.ts import * as path2 from "path"; var config = getConfig(); function pathReport(_path, fieldName) { var _a; if (!(config == null ? void 0 : config.report) || !fieldName || !((_a = config == null ? void 0 : config.report) == null ? void 0 : _a[fieldName])) return path2.join(process.cwd(), _path); return path2.join(process.cwd(), config.report[fieldName], _path); } // src/reporters/exporters/Excel.exporter.ts import * as exceljs from "exceljs"; import * as fs2 from "fs"; import { Writable as Writable2 } from "stream"; // src/common/core/ListEvents.ts var EventRegister = class { constructor() { this.listEvents = {}; } on(key, func) { this.listEvents[key] = func; return this; } onStart(func) { this.listEvents.start = func; return this; } onFinish(func) { this.listEvents.finish = func; return this; } onFile(func) { this.listEvents.onFile = func; return this; } onBegin(func) { this.listEvents.begin = func; return this; } onData(func) { this.listEvents.data = func; return this; } endData(func) { this.listEvents.enddata = func; return this; } onEnd(func) { this.listEvents.end = func; return this; } onError(func) { this.listEvents.error = func; return this; } onHeader(func) { this.listEvents.header = func; return this; } onFooter(func) { this.listEvents.footer = func; return this; } emitEvent(key, data) { if (this.listEvents[key]) this.listEvents[key](data); return this; } }; // src/helpers/get-file-extension.ts function getFileExtension(path3) { const fileName = path3.replace("\\", "/").split("/").pop(); if (!fileName) throw new Error(`Path [${path3}] invalid`); const extension = fileName.split(".").pop(); if (!extension) throw new Error(`Path [${path3}] invalid`); if (extension === "js") return "js"; else if (extension === "ts") return "ts"; else throw new Error(`Path [${path3}] invalid`); } // src/helpers/sort-by-address.ts function reverseString(str) { return str.split("").reverse().join(""); } function sortByAddress(arr, mode = "ASC") { return arr.sort((a, b) => { if (!a.address) return 1; if (!b.address) return -1; if (a.address === b.address) return 0; const result = reverseString(a.address).localeCompare(reverseString(b.address)); return result * (mode === "DESC" ? -1 : 1); }); } // src/common/core/Template.ts var ExcelTemplateManager = class { constructor(templatePath) { this.sheets = []; this.currentSheetIndex = 0; this.groupCells = {}; this.isNullTemplate = false; this.isNullTemplate = !templatePath; this.sheets = templatePath ? this.getTemplate(templatePath) : []; } get ActualTableStartRow() { return this.actualTableStartRow; } get ActualTableEndRow() { return this.actualTableEndRow; } defineActualTableStartRow(actualTableStartRow) { if (!actualTableStartRow || actualTableStartRow <= 0) return; if (this.ActualTableStartRow) return; this.actualTableStartRow = actualTableStartRow; } defineActualTableEndRow(actualTableEndRow) { if (!actualTableEndRow || actualTableEndRow <= 0) return; if (this.ActualTableEndRow) return; this.actualTableEndRow = actualTableEndRow; } set SheetIndex(sheetIndex) { if (sheetIndex < 0) return; this.currentSheetIndex = sheetIndex; this.groupCells = this.formatSheet(); } get SheetInformation() { return this.SheetTemplate; } get SheetTemplate() { return this.sheets[this.currentSheetIndex]; } get Sheets() { return this.sheets; } get GroupCells() { return this.groupCells; } get(arg) { if (this.isNullTemplate) throw new Error("Template is null. Please check template path"); let index = 0; if (typeof arg === "string") index = this.findIndexByKeyName(arg); else if (typeof arg === "number") index = arg; return this.SheetTemplate.cells[index]; } add(cell) { if (this.isNullTemplate) throw new Error("Template is null. Please check template path"); if (!Array.isArray(cell)) cell = [cell]; this.SheetTemplate.cells.push(...cell); } update(key, cell) { if (this.isNullTemplate) throw new Error("Template is null. Please check template path"); let index = 0; if (typeof key === "string") index = this.findIndexByKeyName(key); else if (typeof key === "number") index = key; this.SheetTemplate.cells[index] = cell; } remove(key) { if (this.isNullTemplate) throw new Error("Template is null. Please check template path"); let index = 0; if (typeof key === "string") index = this.findIndexByKeyName(key); else if (typeof key === "number") index = key; this.SheetTemplate.cells.splice(index, 1); } findIndexByKeyName(key) { const index = this.SheetTemplate.cells.findIndex((e) => e.keyName === key); if (index < 0) throw new Error("Not found template with key: " + key); return index; } getTemplate(templatePath) { const template = getFileExtension(templatePath) === "js" ? __require(templatePath) : __require(templatePath).default; return template.sheets; } formatSheet() { var _a; const defaultGroup = {}; if (this.isNullTemplate) return defaultGroup; const excel = (_a = this.SheetTemplate) == null ? void 0 : _a.cells.reduce((acc, cell) => { var _a2; if (!acc[cell.section]) acc[cell.section] = [cell]; else (_a2 = acc[cell.section]) == null ? void 0 : _a2.push(cell); return acc; }, {}); const keys = Object.keys(excel); for (let i = 0; i < keys.length; i++) excel[keys[i]] = sortByAddress(excel[keys[i]]); return excel; } }; // src/reporters/SheetMeta.ts var SheetMeta = class { constructor(opts) { this.byJobIndex = true; this.sheetMetas = {}; this.mapSheetByJobIndex = {}; this.currentSheetName = 0; this.rowCount = 0; this.sheetNames = []; this.isCompleted = false; this.jobCount = 0; var _a; for (let i = 0; i < opts.length; i++) if (opts[i].jobs || opts[i].maxRow) this.addSheetMeta(opts[i].name, (_a = opts[i].jobs) != null ? _a : opts[i].maxRow); } // private addSheetMeta(name: string, jobIndexes: number[]): this; // private addSheetMeta(name: string, rowCount: number): this; addSheetMeta(name, data) { if (typeof data === "number") { this.byJobIndex = false; this.sheetMetas[name] = { isCompleted: false, rowCount: 0, maxRow: data }; } else if (Array.isArray(data)) { this.byJobIndex = true; this.sheetMetas[name] = { isCompleted: false, rowCount: 0, jobCount: data.length, maxRow: 0 }; this.jobCount += data.length; data.forEach((e) => { this.mapSheetByJobIndex[e] = name; }); } this.sheetNames.push(name); return this; } completeJob(index) { const name = this.mapSheetByJobIndex[index]; if (this.sheetMetas[name].jobCount) { this.sheetMetas[name].jobCount -= 1; this.sheetMetas[name].isCompleted = this.sheetMetas[name].jobCount <= 0; this.jobCount--; this.isCompleted = this.jobCount <= 0; } } get IsCompleted() { return this.isCompleted; } get RowCount() { return this.rowCount; } updateRowCount(isCompleted, rowCount) { var _a; const name = this.sheetNames[this.currentSheetName]; const sheetMeta = this.sheetMetas[name]; if (isCompleted) sheetMeta.isCompleted = isCompleted; else if (!this.byJobIndex) { this.rowCount += rowCount; if (((_a = sheetMeta.maxRow) != null ? _a : 0) < sheetMeta.rowCount) { this.currentSheetName++; sheetMeta.isCompleted = true; } sheetMeta.rowCount += rowCount; } } getSheetName(index) { if (this.byJobIndex && index) return this.mapSheetByJobIndex[index]; return this.sheetNames[this.currentSheetName]; } getSheetStatus(name) { return this.sheetMetas[name].isCompleted; } }; // src/common/core/QueueData.ts var QueueData = class { constructor(size = 50) { this.cells = []; this.resolveFunc = () => { }; this.size = size; this.waitingFunc = this.createWaiter(); } createWaiter() { return new Promise((resolve) => { this.resolveFunc = resolve; }); } waiting() { return __async(this, null, function* () { if (this.cells.length < this.size) return; yield this.waitingFunc; }); } shift() { const data = this.cells.shift(); if (this.cells.length === this.size - 1) { this.resolveFunc(); } return data; } add(data) { this.cells.push(data); if (this.cells.length === this.size) { this.waitingFunc = this.createWaiter(); } } }; // src/reporters/PartialDataTransfer.ts var PartialDataTransfer = class { constructor(opts) { this.delayMs = 5; this.isStream = false; this.partialDataHandler = {}; this.jobCount = 1; this.queueData = new QueueData(100); this.isStopConsumeData = false; var _a, _b, _c; this.delayMs = (_a = opts == null ? void 0 : opts.delayMs) != null ? _a : 1; this.isStream = (_b = opts == null ? void 0 : opts.isStream) != null ? _b : false; this.jobCount = (_c = opts == null ? void 0 : opts.jobCount) != null ? _c : 1; } init(partialDataHandler, originalSheetName) { return __async(this, null, function* () { const sheetMetaOptions = this.configSheetMeta(originalSheetName); this.partialDataHandler = partialDataHandler; this.partialDataHandler.done = () => __async(this, null, function* () { return yield this.completed(); }); if (sheetMetaOptions) this.partialDataHandler.SheetMeta = new SheetMeta(sheetMetaOptions); yield this.awake(); }); } start() { return __async(this, null, function* () { if (this.isStream) yield this.startStream(); else yield Promise.all([this.startJobs(), this.consumeData()]); }); } /** Run with stream */ startStream() { return __async(this, null, function* () { const readable = this.createStream(); if (readable === null) throw new Error("You must implement 'createStream' method when using isStream = true"); const wriable = this.partialDataHandler.stream(); readable.pipe(wriable); }); } consumeData() { return __async(this, null, function* () { while (!this.isStopConsumeData) { const data = this.queueData.shift(); if (data) { const isCompleted = yield this.partialDataHandler.do(data); if (isCompleted) break; } else { yield new Promise((r) => setTimeout(r, 2)); } } }); } /** Run with one or multiples job */ startJobs() { return __async(this, null, function* () { const promises = []; const that = this; for (let i = 0; i < this.jobCount; i++) { promises.push(that.createJob(i)); } yield Promise.all(promises); }); } createJob(i) { return __async(this, null, function* () { let isLoop = true; while (isLoop) { const { hasNext, items } = yield this.fetchBatch(i); yield this.queueData.waiting(); this.queueData.add({ items, jobIndex: i }); isLoop = hasNext; yield new Promise((resolve) => setTimeout(resolve, this.delayMs)); } }); } configSheetMeta(originalSheetName) { return void 0; } awake() { return __async(this, null, function* () { }); } completed() { return __async(this, null, function* () { }); } /** Batching data */ fetchBatch(jobIndex) { return __async(this, null, function* () { return { items: null, hasNext: false }; }); } /** Run with streaming data */ createStream() { return null; } }; // src/reporters/exporters/proccessor/ExcelProcessor.ts var ExcelProcessor = class { constructor(opts) { this.template = {}; this.style = "use-style"; this.titlesTable = []; this.columnKeys = []; var _a; this.workBook = opts.workBook; this.headerData = opts.header; this.footerData = opts.footer; this.template = opts.template; this.style = (_a = opts.style) != null ? _a : "use-style"; this.event = opts.event; this.titlesTable = this.template.GroupCells.table.map((e) => { var _a2; return (_a2 = e.value.fieldName) != null ? _a2 : ""; }); this.columnKeys = opts.style !== "no-style-no-header" ? void 0 : this.createColumnKey(); } createColumnKey() { const tableCells = this.template.GroupCells.table; const columns = this.template.GroupCells.header.map((e, i) => { var _a; return { header: e.value.hardValue, key: (_a = tableCells[i].value.fieldName) != null ? _a : "" }; }); return columns; } getOrCreateWorksheet(name) { var _a; return (_a = this.workBook.getWorksheet(name)) != null ? _a : this.workBook.addWorksheet(name); } setHeader(headerData, arg) { if (!this.template.GroupCells.header) return; const workSheet = typeof arg === "string" ? this.getOrCreateWorksheet(arg) : arg; for (let i = 1; i <= this.template.SheetInformation.beginTableAt; i++) { const formats = this.template.GroupCells.header.filter((e) => e.fullAddress.row === i); this.addRow(headerData, workSheet, formats); } this.event.emitEvent("header", workSheet.name); } pushData(sheetName, batches, isFinish = false) { if (!this.workBook.getWorksheet(sheetName)) { this.event.emitEvent("begin", sheetName); const workSheet2 = this.workBook.addWorksheet(sheetName); if (this.columnKeys) workSheet2.columns = this.columnKeys; else this.setHeader(this.headerData, workSheet2); } const workSheet = this.getOrCreateWorksheet(sheetName); if (isFinish) { this.setFooter(this.footerData, workSheet); this.finalizeWorksheet(sheetName); return; } if (Array.isArray(batches)) { for (let i = 0; i < batches.length; i++) { if (this.style === "no-style-no-header") workSheet.addRow(batches[i]).commit(); else if (this.style === "use-style") this.addRow(batches[i], workSheet, this.template.GroupCells.table); else this.addRowWithoutStyle(batches[i], workSheet); } } else if (batches !== null) this.addRow(batches, workSheet, this.template.GroupCells.table); } setFooter(footerData, arg) { const workSheet = typeof arg === "string" ? this.getOrCreateWorksheet(arg) : arg; if (this.template.GroupCells.footer) { const groupFooter = this.template.GroupCells.footer.sort((a, b) => b.fullAddress.row - a.fullAddress.row).reduce((acc, value) => { const row = value.fullAddress.row; acc[row] = acc[row] ? [...acc[row], value] : [value]; return acc; }, {}); for (const [rowIndex, footers] of Object.entries(groupFooter)) { this.addRow(footerData, workSheet, footers); } } if (this.style === "use-style") this.mergeCells(workSheet); if (this.style === "use-style") this.setWidthsAndHeights(workSheet); this.event.emitEvent("footer", workSheet.name); } finalizeWorksheet(sheetName) { this.event.emitEvent("end", sheetName); } addRowWithoutStyle(rowdata, workSheet) { const row = []; for (let i = 0; i < this.titlesTable.length; i++) { row.push(rowdata[this.titlesTable[i]]); } workSheet.addRow(row).commit(); } addRow(data, workSheet, cellDesc) { const row = workSheet.addRow([]); for (let i = 0; i < cellDesc.length; i++) { const cellsOpt = cellDesc[i]; const cell = row.getCell(cellsOpt.fullAddress.col); this.setCell(data, cellsOpt, cell); } return row; } setCell(rowData, cellOpt, cell) { if (this.style === "use-style") cell.style = cellOpt.style; if (cellOpt.formula) { cell.value = { formula: cellOpt.formula }; } else { let value = cellOpt.isVariable ? rowData[cellOpt.value.fieldName] : cellOpt.value.hardValue; if (cellOpt.formatValue) value = cellOpt.formatValue(value); cell.value = value; } return cell; } mergeCells(sheet) { const merges = this.template.SheetTemplate.merges; if (merges) { Object.keys(merges).forEach((masterCell) => { const { top, left, right, bottom } = merges[masterCell].model; sheet.mergeCells(top, left, bottom, right); }); } } setWidthsAndHeights(sheet) { const columnWidths = this.template.SheetTemplate.columnWidths; const rowHeights = this.template.SheetTemplate.rowHeights; columnWidths == null ? void 0 : columnWidths.forEach((colW, i) => { if ((sheet == null ? void 0 : sheet.columns) && (sheet == null ? void 0 : sheet.columns[i]) && colW) sheet.columns[i].width = colW; }); if (rowHeights) Object.keys(rowHeights).forEach((rowIndex, i) => { if (rowHeights[rowIndex]) sheet.getRow(rowHeights[i]).height = rowHeights[rowIndex]; }); } }; var ExcelStreamProcessor = class extends ExcelProcessor { finalizeWorksheet(sheetName) { const worksheet = this.getOrCreateWorksheet(sheetName); worksheet.commit(); this.event.emitEvent("end", sheetName); } addRow(data, workSheet, cellDesc) { const row = super.addRow(data, workSheet, cellDesc); if (this.style !== "use-style") row.commit(); return row; } finalizeWorkbook() { return __async(this, null, function* () { yield this.workBook.commit(); this.event.emitEvent("finish"); }); } }; // src/reporters/IPartialDataHandler.ts import { Writable } from "stream"; var PartialDataHandler = class { constructor(originalSheetName, task) { this.done = () => __async(this, null, function* () { }); this.task = task; this.originalSheetName = originalSheetName; } set SheetMeta(value) { this.sheetMeta = value; } do(args) { return __async(this, null, function* () { var _a; let sheetName = this.originalSheetName; let sheetCompleted = false; let isCompleted = args.items === null; if (this.sheetMeta) { this.sheetMeta.updateRowCount(isCompleted, (_a = args.items) == null ? void 0 : _a.length); sheetName = this.sheetMeta.getSheetName(args.jobIndex); sheetCompleted = this.sheetMeta.getSheetStatus(sheetName); isCompleted = this.sheetMeta.IsCompleted; if (args.jobIndex && args.items === null) this.sheetMeta.completeJob(args.jobIndex); } yield this.task({ items: args.items, jobIndex: args.jobIndex, isCompleted, sheetCompleted, sheetName }); if (isCompleted) yield this.done(null); return isCompleted; }); } stream() { const that = this; return new Writable({ objectMode: true, write(arg, _encoding, callback) { return __async(this, null, function* () { try { yield that.do({ items: arg }); } catch (err) { return callback(err); } callback(); }); } }); } }; // src/reporters/exporters/Excel.exporter.ts var ExcelExporter = class { constructor(templatePath) { this.template = {}; this.event = new EventRegister(); this.opts = { style: "use-style" }; this.excelProcessor = {}; this.templatePath = templatePath; this.template = new ExcelTemplateManager(this.templatePath); this.template.SheetIndex = 0; } get Template() { return this.template; } get Event() { return this.event; } write(reportPath, data, opts) { return __async(this, null, function* () { const workBook = new exceljs.Workbook(); this.Event.emitEvent("onFile"); yield this.execute(workBook, data); yield workBook.xlsx.writeFile(reportPath); }); } toBuffer(data, opts) { return __async(this, null, function* () { const workBook = new exceljs.Workbook(); this.Event.emitEvent("onFile"); yield this.execute(workBook, data); return yield workBook.xlsx.writeBuffer(); }); } streamTo(arg1, data, opts) { var _a; this.opts = { style: (_a = opts == null ? void 0 : opts.style) != null ? _a : "use-style" }; const stream2 = arg1 instanceof Writable2 ? arg1 : fs2.createWriteStream(arg1); const workBook = new exceljs.stream.xlsx.WorkbookWriter({ stream: stream2, useSharedStrings: opts == null ? void 0 : opts.useSharedStrings, useStyles: (opts == null ? void 0 : opts.style) === "use-style" ? true : false, zip: opts == null ? void 0 : opts.zip }); this.Event.emitEvent("onFile"); this.execute(workBook, data, ExcelStreamProcessor); } execute(_0, _1) { return __async(this, arguments, function* (workBook, data, classProcessor = ExcelProcessor) { this.excelProcessor = new classProcessor({ workBook, template: this.Template, event: this.event, header: data.header, footer: data.footer, style: this.opts.style }); const originalSheetName = this.Template.SheetInformation.sheetName; const partialDataHandler = new PartialDataHandler(originalSheetName, this.createTask()); this.Event.emitEvent("start"); if (data.table instanceof PartialDataTransfer) { const tableData = data.table; yield tableData.init(partialDataHandler, originalSheetName); yield tableData.start(); } else { const table = data.table; for (let i = 0; i < table.length; i++) this.excelProcessor.pushData(originalSheetName, table[i], false); this.excelProcessor.pushData(originalSheetName, null, true); } }); } createTask() { return (args) => __async(this, null, function* () { var _a; this.excelProcessor.pushData((_a = args.sheetName) != null ? _a : "", args.items, args.sheetCompleted); if (this.excelProcessor instanceof ExcelStreamProcessor && args.isCompleted) { yield this.excelProcessor.finalizeWorkbook(); } }); } }; // src/reporters/exporters/Html.exporter.ts import * as ejs from "ejs"; var HtmlExporter = class { constructor(templatePath) { this.templatePath = templatePath; } write(reportPath, data) { return __async(this, null, function* () { ejs.render(this.templatePath, data); }); } toBuffer(data) { return __async(this, null, function* () { return Buffer.from(ejs.render(this.templatePath, data)); }); } streamTo(...args) { throw new Error("Stream Method don't support for html."); } }; // src/reporters/exporters/Pdf.exporter.ts import * as ejs2 from "ejs"; import * as fs3 from "fs"; import * as puppeteer from "puppeteer"; var PdfExporter = class { constructor(templatePath) { this.templatePath = templatePath; } write(reportPath, data, opts) { return __async(this, null, function* () { const { browser, content } = yield this.genTemplate(data, opts); yield browser.close(); fs3.writeFile(reportPath, content, () => console.log("Write file pdf successfully")); }); } toBuffer(data, opts) { return __async(this, null, function* () { const { browser, content } = yield this.genTemplate(data, opts); yield browser.close(); return content; }); } genTemplate(data, opts) { return __async(this, null, function* () { var _a; const html = ejs2.render((_a = this.templatePath) != null ? _a : "", data); const browser = yield puppeteer.launch({ args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-gpu", "--disable-dev-shm-usage", "--no-zygote"] }); const page = yield browser.newPage(); yield page.setContent(html, { waitUntil: "networkidle0" }); return { content: yield page.pdf({ format: "A4", printBackground: true, margin: { top: "20px", bottom: "20px" } }), browser }; }); } streamTo(...args) { throw new Error("Method not implemented."); } }; // src/reporters/Reporter.ts var Reporter = class { constructor(templatePath) { var _a; this.templatePath = pathReport(templatePath, "templateDir"); this.templatePath = `${this.templatePath}${(_a = getConfig().templateExtension) != null ? _a : ".js"}`; } createExporterEXCEL() { return new ExcelExporter(this.templatePath); } // createExporterCSV() {} createExportePDF() { return new PdfExporter(this.templatePath); } createExporterHTML() { return new HtmlExporter(this.templatePath); } }; export { Reporter };