ExcelJS Spreadsheet Guide: Reading, Writing, Formatting, and VFS Integration # Goal Provide actionable instructions, code patterns, and best practices for creating, reading, modifying, and styling Excel (`.xlsx`) and CSV spreadsheets in the `javascript_execute` sandbox using `ExcelJS`. # Environment & Globals - Both `ExcelJS` and `exceljs` are pre-injected in global scope. Do NOT use `require()` or `import`. - `Workbook`, `stream.xlsx.WorkbookWriter`, and `stream.xlsx.WorkbookReader` are pre-bridged to the Virtual Filesystem (VFS). - Wrap async scripts in an IIFE: `module.exports = (async () => { ... })();`. --- # 1. Creating and Populating a Workbook ```javascript module.exports = (async () => { const wb = new ExcelJS.Workbook(); wb.creator = 'QuixPOS Assistant'; wb.created = new Date(); const ws = wb.addWorksheet('Articles', { properties: { tabColor: { argb: 'FF1F4E79' } }, views: [{ state: 'frozen', xSplit: 0, ySplit: 1 }] // freeze header row }); // Define column schemas ws.columns = [ { header: 'SKU', key: 'sku', width: 14 }, { header: 'Title', key: 'title', width: 36 }, { header: 'Price (Gross)', key: 'price', width: 14, style: { numFmt: '#,##0.00 €' } }, { header: 'Stock', key: 'stock', width: 10, style: { numFmt: '#,##0' } }, { header: 'Supplier SKU', key: 'supplierSku', width: 16 } ]; // Add rows via object matching column keys ws.addRow({ sku: 'ART-1001', title: 'Espresso Arabica Premium', price: 14.99, stock: 45, supplierSku: 'SUP-01' }); ws.addRow({ sku: 'ART-1002', title: 'Earl Grey Organic Tea', price: 8.50, stock: 20, supplierSku: 'SUP-02' }); // Or add multiple rows at once ws.addRows([ { sku: 'ART-1003', title: 'Single Origin Colombia', price: 16.20, stock: 12, supplierSku: 'SUP-03' }, { sku: 'ART-1004', title: 'Matcha Ceremonial Grade', price: 24.00, stock: 8, supplierSku: 'SUP-04' } ]); // Save directly to VFS await wb.xlsx.writeFile('articles-export.xlsx'); return { success: true, rows: ws.rowCount, file: 'articles-export.xlsx' }; })(); ``` --- # 2. Header & Cell Styling ```javascript // Header styling: bold white text with dark blue background const headerRow = ws.getRow(1); headerRow.height = 24; headerRow.font = { name: 'Arial', size: 11, bold: true, color: { argb: 'FFFFFFFF' } }; headerRow.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF1F4E79' } }; headerRow.alignment = { vertical: 'middle', horizontal: 'center' }; // Zebra striping data rows for (let i = 2; i <= ws.rowCount; i++) { const row = ws.getRow(i); row.height = 20; row.alignment = { vertical: 'middle' }; if (i % 2 === 0) { row.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFF9FAFB' } }; } // Thin borders row.eachCell({ includeEmpty: true }, (cell) => { cell.border = { bottom: { style: 'thin', color: { argb: 'FFE5E7EB' } } }; }); } ``` --- # 3. Formulas & Summary Rows ```javascript // Add a total row at the end const totalRow = ws.addRow({ title: 'Total Stock & Value', price: { formula: 'SUMPRODUCT(C2:C5, D2:D5)', result: 0 }, stock: { formula: 'SUM(D2:D5)', result: 85 } }); totalRow.font = { bold: true }; totalRow.getCell('title').alignment = { horizontal: 'right' }; totalRow.border = { top: { style: 'thin', color: { argb: 'FF000000' } }, bottom: { style: 'double', color: { argb: 'FF000000' } } }; ``` --- # 4. Reading & Modifying Existing Spreadsheets from VFS ```javascript module.exports = (async () => { const wb = new ExcelJS.Workbook(); // Path is relative to VFS (e.g. user attachment or previous artifact) await wb.xlsx.readFile('attachments/inventory.xlsx'); const ws = wb.getWorksheet(1); // 1-based index or name 'Sheet1' const records = []; ws.eachRow({ includeEmpty: false }, (row, rowNumber) => { if (rowNumber === 1) return; // skip header records.push({ sku: row.getCell(1).text, name: row.getCell(2).text, price: Number(row.getCell(3).value) || 0, stock: Number(row.getCell(4).value) || 0 }); }); // You can modify cells and write back ws.getCell('C2').value = 19.99; await wb.xlsx.writeFile('attachments/inventory-updated.xlsx'); return { totalRead: records.length, records }; })(); ``` --- # 5. Working with CSV Files ```javascript // Read CSV from VFS const csvWb = new ExcelJS.Workbook(); await csvWb.csv.readFile('data.csv'); const sheet = csvWb.getWorksheet(1); // Write CSV to VFS await csvWb.csv.writeFile('export.csv'); ``` --- # 6. Streaming Massive Datasets For large tables (>5,000 rows) use `WorkbookWriter` to prevent high memory consumption: ```javascript module.exports = (async () => { const writer = new ExcelJS.stream.xlsx.WorkbookWriter({ filename: 'large-catalog.xlsx' }); const ws = writer.addWorksheet('Catalog'); ws.columns = [ { header: 'ID', key: 'id', width: 10 }, { header: 'Name', key: 'name', width: 30 }, { header: 'Stock', key: 'stock', width: 12 } ]; for (let i = 1; i <= 10000; i++) { ws.addRow({ id: i, name: `Product ${i}`, stock: Math.floor(Math.random() * 100) }).commit(); } ws.commit(); await writer.commit(); return { exportedRows: 10000, file: 'large-catalog.xlsx' }; })(); ``` --- # 7. Embedding Product / Company Images ```javascript // Image filename is automatically resolved from VFS const logoId = wb.addImage({ filename: 'images/logo.png', extension: 'png' }); // Position image by cell coordinates ws.addImage(logoId, { tl: { col: 5, row: 1 }, ext: { width: 120, height: 40 } }); ``` --- # 8. Presenting Output to User When you generate or modify an Excel file in `javascript_execute`, **always include a clickable Markdown link in your response summary**: - `[Download Articles Spreadsheet](articles-export.xlsx)` - `[Download Updated Inventory](attachments/inventory-updated.xlsx)`