|
| 1 | +#!/usr/bin/env node |
| 2 | +/* index.js (C) 2020-present SheetJS -- http://sheetjs.com */ |
| 3 | +/* eslint-env node */ |
| 4 | +/* vim: set ts=2 ft=javascript: */ |
| 5 | + |
| 6 | +var n = "xlsx-cli"; |
| 7 | +var X = require('xlsx'); |
| 8 | +require('exit-on-epipe'); |
| 9 | +var fs = require('fs'), program = require('commander'); |
| 10 | +function run() { |
| 11 | + program |
| 12 | + .version(X.version) |
| 13 | + .usage('[options] <file> [sheetname]') |
| 14 | + .option('-f, --file <file>', 'use specified workbook') |
| 15 | + .option('-s, --sheet <sheet>', 'print specified sheet (default first sheet)') |
| 16 | + .option('-N, --sheet-index <idx>', 'use specified sheet index (0-based)') |
| 17 | + .option('-p, --password <pw>', 'if file is encrypted, try with specified pw') |
| 18 | + .option('-l, --list-sheets', 'list sheet names and exit') |
| 19 | + .option('-o, --output <file>', 'output to specified file') |
| 20 | + |
| 21 | + .option('-B, --xlsb', 'emit XLSB to <sheetname> or <file>.xlsb') |
| 22 | + .option('-M, --xlsm', 'emit XLSM to <sheetname> or <file>.xlsm') |
| 23 | + .option('-X, --xlsx', 'emit XLSX to <sheetname> or <file>.xlsx') |
| 24 | + .option('-I, --xlam', 'emit XLAM to <sheetname> or <file>.xlam') |
| 25 | + .option('-Y, --ods', 'emit ODS to <sheetname> or <file>.ods') |
| 26 | + .option('-8, --xls', 'emit XLS to <sheetname> or <file>.xls (BIFF8)') |
| 27 | + .option('-5, --biff5', 'emit XLS to <sheetname> or <file>.xls (BIFF5)') |
| 28 | + //.option('-4, --biff4','emit XLS to <sheetname> or <file>.xls (BIFF4)') |
| 29 | + //.option('-3, --biff3','emit XLS to <sheetname> or <file>.xls (BIFF3)') |
| 30 | + .option('-2, --biff2', 'emit XLS to <sheetname> or <file>.xls (BIFF2)') |
| 31 | + .option('-i, --xla', 'emit XLA to <sheetname> or <file>.xla') |
| 32 | + .option('-6, --xlml', 'emit SSML to <sheetname> or <file>.xls (2003 XML)') |
| 33 | + .option('-T, --fods', 'emit FODS to <sheetname> or <file>.fods (Flat ODS)') |
| 34 | + |
| 35 | + .option('-S, --formulae', 'emit list of values and formulae') |
| 36 | + .option('-j, --json', 'emit formatted JSON (all fields text)') |
| 37 | + .option('-J, --raw-js', 'emit raw JS object (raw numbers)') |
| 38 | + .option('-A, --arrays', 'emit rows as JS objects (raw numbers)') |
| 39 | + .option('-H, --html', 'emit HTML to <sheetname> or <file>.html') |
| 40 | + .option('-D, --dif', 'emit DIF to <sheetname> or <file>.dif (Lotus DIF)') |
| 41 | + .option('-U, --dbf', 'emit DBF to <sheetname> or <file>.dbf (MSVFP DBF)') |
| 42 | + .option('-K, --sylk', 'emit SYLK to <sheetname> or <file>.slk (Excel SYLK)') |
| 43 | + .option('-P, --prn', 'emit PRN to <sheetname> or <file>.prn (Lotus PRN)') |
| 44 | + .option('-E, --eth', 'emit ETH to <sheetname> or <file>.eth (Ethercalc)') |
| 45 | + .option('-t, --txt', 'emit TXT to <sheetname> or <file>.txt (UTF-8 TSV)') |
| 46 | + .option('-r, --rtf', 'emit RTF to <sheetname> or <file>.txt (Table RTF)') |
| 47 | + .option('-z, --dump', 'dump internal representation as JSON') |
| 48 | + .option('--props', 'dump workbook properties as CSV') |
| 49 | + |
| 50 | + .option('-F, --field-sep <sep>', 'CSV field separator', ",") |
| 51 | + .option('-R, --row-sep <sep>', 'CSV row separator', "\n") |
| 52 | + .option('-n, --sheet-rows <num>', 'Number of rows to process (0=all rows)') |
| 53 | + .option('--codepage <cp>', 'default to specified codepage when ambiguous') |
| 54 | + .option('--req <module>', 'require module before processing') |
| 55 | + .option('--sst', 'generate shared string table for XLS* formats') |
| 56 | + .option('--compress', 'use compression when writing XLSX/M/B and ODS') |
| 57 | + .option('--read', 'read but do not generate output') |
| 58 | + .option('--book', 'for single-sheet formats, emit a file per worksheet') |
| 59 | + .option('--all', 'parse everything; write as much as possible') |
| 60 | + .option('--dev', 'development mode') |
| 61 | + .option('--sparse', 'sparse mode') |
| 62 | + .option('-q, --quiet', 'quiet mode'); |
| 63 | + |
| 64 | + program.on('--help', function () { |
| 65 | + console.log(' Default output format is CSV'); |
| 66 | + console.log(' Support email: [email protected]'); |
| 67 | + console.log(' Web Demo: http://oss.sheetjs.com/js-' + n + '/'); |
| 68 | + }); |
| 69 | + |
| 70 | + /* flag, bookType, default ext */ |
| 71 | + var workbook_formats = [ |
| 72 | + ['xlsx', 'xlsx', 'xlsx'], |
| 73 | + ['xlsm', 'xlsm', 'xlsm'], |
| 74 | + ['xlam', 'xlam', 'xlam'], |
| 75 | + ['xlsb', 'xlsb', 'xlsb'], |
| 76 | + ['xls', 'xls', 'xls'], |
| 77 | + ['xla', 'xla', 'xla'], |
| 78 | + ['biff5', 'biff5', 'xls'], |
| 79 | + ['ods', 'ods', 'ods'], |
| 80 | + ['fods', 'fods', 'fods'] |
| 81 | + ]; |
| 82 | + var wb_formats_2 = [ |
| 83 | + ['xlml', 'xlml', 'xls'] |
| 84 | + ]; |
| 85 | + program.parse(process.argv); |
| 86 | + |
| 87 | + var filename = '', sheetname = ''; |
| 88 | + if (program.args[0]) { |
| 89 | + filename = program.args[0]; |
| 90 | + if (program.args[1]) sheetname = program.args[1]; |
| 91 | + } |
| 92 | + if (program.sheet) sheetname = program.sheet; |
| 93 | + if (program.file) filename = program.file; |
| 94 | + |
| 95 | + if (!filename) { |
| 96 | + console.error(n + ": must specify a filename"); |
| 97 | + process.exit(1); |
| 98 | + } |
| 99 | + if (!fs.existsSync(filename)) { |
| 100 | + console.error(n + ": " + filename + ": No such file or directory"); |
| 101 | + process.exit(2); |
| 102 | + } |
| 103 | + |
| 104 | + if (program.req) program.req.split(",").forEach(function (r) { |
| 105 | + require((fs.existsSync(r) || fs.existsSync(r + '.js')) ? require('path').resolve(r) : r); |
| 106 | + }); |
| 107 | + |
| 108 | + var opts = {}, wb/*:?Workbook*/; |
| 109 | + if (program.listSheets) opts.bookSheets = true; |
| 110 | + if (program.sheetRows) opts.sheetRows = program.sheetRows; |
| 111 | + if (program.password) opts.password = program.password; |
| 112 | + var seen = false; |
| 113 | + function wb_fmt() { |
| 114 | + seen = true; |
| 115 | + opts.cellFormula = true; |
| 116 | + opts.cellNF = true; |
| 117 | + if (program.output) sheetname = program.output; |
| 118 | + } |
| 119 | + function isfmt(m/*:string*/)/*:boolean*/ { |
| 120 | + if (!program.output) return false; |
| 121 | + var t = m.charAt(0) === "." ? m : "." + m; |
| 122 | + return program.output.slice(-t.length) === t; |
| 123 | + } |
| 124 | + workbook_formats.forEach(function (m) { if (program[m[0]] || isfmt(m[0])) { wb_fmt(); } }); |
| 125 | + wb_formats_2.forEach(function (m) { if (program[m[0]] || isfmt(m[0])) { wb_fmt(); } }); |
| 126 | + if (seen) { |
| 127 | + } else if (program.formulae) opts.cellFormula = true; |
| 128 | + else opts.cellFormula = false; |
| 129 | + |
| 130 | + var wopts = ({ WTF: opts.WTF, bookSST: program.sst }/*:any*/); |
| 131 | + if (program.compress) wopts.compression = true; |
| 132 | + |
| 133 | + if (program.all) { |
| 134 | + opts.cellFormula = true; |
| 135 | + opts.bookVBA = true; |
| 136 | + opts.cellNF = true; |
| 137 | + opts.cellHTML = true; |
| 138 | + opts.cellStyles = true; |
| 139 | + opts.sheetStubs = true; |
| 140 | + opts.cellDates = true; |
| 141 | + wopts.cellStyles = true; |
| 142 | + wopts.sheetStubs = true; |
| 143 | + wopts.bookVBA = true; |
| 144 | + } |
| 145 | + if (program.sparse) opts.dense = false; else opts.dense = true; |
| 146 | + if (program.codepage) opts.codepage = +program.codepage; |
| 147 | + |
| 148 | + if (program.dev) { |
| 149 | + opts.WTF = true; |
| 150 | + wb = X.readFile(filename, opts); |
| 151 | + } else try { |
| 152 | + wb = X.readFile(filename, opts); |
| 153 | + } catch (e) { |
| 154 | + var msg = (program.quiet) ? "" : n + ": error parsing "; |
| 155 | + msg += filename + ": " + e; |
| 156 | + console.error(msg); |
| 157 | + process.exit(3); |
| 158 | + } |
| 159 | + if (program.read) process.exit(0); |
| 160 | + if (!wb) { console.error(n + ": error parsing " + filename + ": empty workbook"); process.exit(0); } |
| 161 | + /*:: if(!wb) throw new Error("unreachable"); */ |
| 162 | + if (program.listSheets) { |
| 163 | + console.log((wb.SheetNames || []).join("\n")); |
| 164 | + process.exit(0); |
| 165 | + } |
| 166 | + if (program.dump) { |
| 167 | + console.log(JSON.stringify(wb)); |
| 168 | + process.exit(0); |
| 169 | + } |
| 170 | + if (program.props) { |
| 171 | + if (wb) dump_props(wb); |
| 172 | + process.exit(0); |
| 173 | + } |
| 174 | + |
| 175 | + /* full workbook formats */ |
| 176 | + workbook_formats.forEach(function (m) { |
| 177 | + if (program[m[0]] || isfmt(m[0])) { |
| 178 | + wopts.bookType = m[1]; |
| 179 | + if (wb) X.writeFile(wb, program.output || sheetname || ((filename || "") + "." + m[2]), wopts); |
| 180 | + process.exit(0); |
| 181 | + } |
| 182 | + }); |
| 183 | + |
| 184 | + wb_formats_2.forEach(function (m) { |
| 185 | + if (program[m[0]] || isfmt(m[0])) { |
| 186 | + wopts.bookType = m[1]; |
| 187 | + if (wb) X.writeFile(wb, program.output || sheetname || ((filename || "") + "." + m[2]), wopts); |
| 188 | + process.exit(0); |
| 189 | + } |
| 190 | + }); |
| 191 | + |
| 192 | + var target_sheet = sheetname || ''; |
| 193 | + if (target_sheet === '') { |
| 194 | + if (+program.sheetIndex < (wb.SheetNames || []).length) target_sheet = wb.SheetNames[+program.sheetIndex]; |
| 195 | + else target_sheet = (wb.SheetNames || [""])[0]; |
| 196 | + } |
| 197 | + |
| 198 | + var ws; |
| 199 | + try { |
| 200 | + ws = wb.Sheets[target_sheet]; |
| 201 | + if (!ws) { |
| 202 | + console.error("Sheet " + target_sheet + " cannot be found"); |
| 203 | + process.exit(3); |
| 204 | + } |
| 205 | + } catch (e) { |
| 206 | + console.error(n + ": error parsing " + filename + " " + target_sheet + ": " + e); |
| 207 | + process.exit(4); |
| 208 | + } |
| 209 | + |
| 210 | + if (!program.quiet && !program.book) console.error(target_sheet); |
| 211 | + |
| 212 | + /* single worksheet file formats */ |
| 213 | + [ |
| 214 | + ['biff2', '.xls'], |
| 215 | + ['biff3', '.xls'], |
| 216 | + ['biff4', '.xls'], |
| 217 | + ['sylk', '.slk'], |
| 218 | + ['html', '.html'], |
| 219 | + ['prn', '.prn'], |
| 220 | + ['eth', '.eth'], |
| 221 | + ['rtf', '.rtf'], |
| 222 | + ['txt', '.txt'], |
| 223 | + ['dbf', '.dbf'], |
| 224 | + ['dif', '.dif'] |
| 225 | + ].forEach(function (m) { |
| 226 | + if (program[m[0]] || isfmt(m[1])) { |
| 227 | + wopts.bookType = m[0]; |
| 228 | + if (program.book) { |
| 229 | + /*:: if(wb == null) throw new Error("Unreachable"); */ |
| 230 | + wb.SheetNames.forEach(function (n, i) { |
| 231 | + wopts.sheet = n; |
| 232 | + X.writeFile(wb, (program.output || sheetname || filename || "") + m[1] + "." + i, wopts); |
| 233 | + }); |
| 234 | + } else X.writeFile(wb, program.output || sheetname || ((filename || "") + m[1]), wopts); |
| 235 | + process.exit(0); |
| 236 | + } |
| 237 | + }); |
| 238 | + |
| 239 | + function outit(o, fn) { if (fn) fs.writeFileSync(fn, o); else console.log(o); } |
| 240 | + |
| 241 | + function doit(cb) { |
| 242 | + /*:: if(!wb) throw new Error("unreachable"); */ |
| 243 | + if (program.book) wb.SheetNames.forEach(function (n, i) { |
| 244 | + /*:: if(!wb) throw new Error("unreachable"); */ |
| 245 | + outit(cb(wb.Sheets[n]), (program.output || sheetname || filename) + "." + i); |
| 246 | + }); |
| 247 | + else outit(cb(ws), program.output); |
| 248 | + } |
| 249 | + |
| 250 | + var jso = {}; |
| 251 | + switch (true) { |
| 252 | + case program.formulae: |
| 253 | + doit(function (ws) { return X.utils.sheet_to_formulae(ws).join("\n"); }); |
| 254 | + break; |
| 255 | + |
| 256 | + case program.arrays: jso.header = 1; |
| 257 | + /* falls through */ |
| 258 | + case program.rawJs: jso.raw = true; |
| 259 | + /* falls through */ |
| 260 | + case program.json: |
| 261 | + doit(function (ws) { return JSON.stringify(X.utils.sheet_to_json(ws, jso)); }); |
| 262 | + break; |
| 263 | + |
| 264 | + default: |
| 265 | + if (!program.book) { |
| 266 | + var stream = X.stream.to_csv(ws, { FS: program.fieldSep, RS: program.rowSep }); |
| 267 | + if (program.output) stream.pipe(fs.createWriteStream(program.output)); |
| 268 | + else stream.pipe(process.stdout); |
| 269 | + } else doit(function (ws) { return X.utils.sheet_to_csv(ws, { FS: program.fieldSep, RS: program.rowSep }); }); |
| 270 | + break; |
| 271 | + } |
| 272 | + |
| 273 | + function dump_props(wb/*:Workbook*/) { |
| 274 | + var propaoa = []; |
| 275 | + if (Object.assign && Object.entries) propaoa = Object.entries(Object.assign({}, wb.Props, wb.Custprops)); |
| 276 | + else { |
| 277 | + var Keys/*:: :Array<string> = []*/, pi; |
| 278 | + if (wb.Props) { |
| 279 | + Keys = Object.keys(wb.Props); |
| 280 | + for (pi = 0; pi < Keys.length; ++pi) { |
| 281 | + if (Object.prototype.hasOwnProperty.call(Keys, Keys[pi])) propaoa.push([Keys[pi], Keys[/*::+*/Keys[pi]]]); |
| 282 | + } |
| 283 | + } |
| 284 | + if (wb.Custprops) { |
| 285 | + Keys = Object.keys(wb.Custprops); |
| 286 | + for (pi = 0; pi < Keys.length; ++pi) { |
| 287 | + if (Object.prototype.hasOwnProperty.call(Keys, Keys[pi])) propaoa.push([Keys[pi], Keys[/*::+*/Keys[pi]]]); |
| 288 | + } |
| 289 | + } |
| 290 | + } |
| 291 | + console.log(X.utils.sheet_to_csv(X.utils.aoa_to_sheet(propaoa))); |
| 292 | + } |
| 293 | +} |
| 294 | + |
| 295 | +module.exports = run; |
0 commit comments