UNPKG

read-excel-file

Version:

Read `.xlsx` files in a web browser or in Node.js

270 lines (261 loc) 11.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EMPTY_CELL = void 0; exports["default"] = parseCell; var _parseExcelTimestamp = _interopRequireDefault(require("./parseExcelTimestamp.js")); var _isDateFormatStyle = _interopRequireDefault(require("./isDateFormatStyle.js")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } // An empty cell has `null` value. var EMPTY_CELL_VALUE = null; // This variable represents a cell with a `null` value. // It is exported only to be specified as a dependency when using `worker-f`. var EMPTY_CELL = exports.EMPTY_CELL = [null, EMPTY_CELL_VALUE]; /** * Parses a cell from the info extracted from XML. * @param {string} [t] — `<c t/>` attribute value (cell type). One of: b (Boolean), e (Error), n (Number), d (Date), s (String). * @param {string} [s] — `<c s/>` attribute value (formatting style ID). When present, should be a stringified zero-based index of the formatting style for a numberic cell. * @param {string} [v] — `<v/>` element text content (value). Will be `undefined` if the `<v/>` element absent. Will be an empty string `""` if the `<v/>` element is present but is empty. * @param {string} [inlineString] — Inline string value. * @param {any[]} parameters * @returns {[string|null,string|number|boolean|null] | string} Either `[type, value]` or `error`, where `value` is the cell value, `type` depends on the type of `value` and could be one of: 's' (string), 'b' (boolean), 'n' (number string), 'd' (date timestamp), 'e' (formula cell error), `null` (null); `error` is an error message: `VALUE_MISSING`, `VALUE_INVALID`, `FORMAT_INVALID`, `TYPE_INVALID`. */ function parseCell(t, s, v, inlineString, _ref) { var _ref2 = _slicedToArray(_ref, 7), sharedStrings = _ref2[0], styles = _ref2[1], epoch1904 = _ref2[2], dateFormatDetectionCache = _ref2[3], defaultDateFormat = _ref2[4], dateTemplateParser = _ref2[5], parseNumberCustom = _ref2[6]; // Available Excel cell types: // https://github.com/SheetJS/sheetjs/blob/19620da30be2a7d7b9801938a0b9b1fd3c4c4b00/docbits/52_datatype.md // // Some other document (seems to be old): // http://webapp.docx4java.org/OnlineDemo/ecma376/SpreadsheetML/ST_CellType.html // // The default cell type is "n" (numeric), according to XLSX specification. // switch (t || 'n') { // `t="str"` means that the cell value is calculated using a formula. // The formula is defined as the text of a child `<f/>` element. // // It could optionally include a `<v/>` element whose text is the cached result // of the calculation from the last time the file was saved in a spreadsheet editor application. // // An optional `<v/>` element holds a pre-computed result of the formula defined by `<f/>`. // // Example: // // <c r="B3" t="str"> // <f>CONCATENATE(C1,D1)</f> // <v>C1ValueD1Value</v> // </c> // // Here's a guide on formulas in XLSX files: // https://github.com/MiniMax-AI/skills/blob/main/skills/minimax-xlsx/references/validate.md // case 'str': // The `<v/>` element could be absent because it's not required to be pre-computed by the spec. // In such case, `v` argument value would be `undefined`. // Because this package can't include the whole formula calculation engine, // it has to interpret this situation as an error. if (v === undefined) { return 'VALUE_MISSING'; } // The `<v/>` element could be present but its text content could be empty // because the formula returns an empty string. // For example, a valid case of `v` argument value being an empty string `""` is when // a formula in cell `A1` is `=CONCATENATE(B1,C1)` and both `B1` and `C1` cells are empty. if (!v) { return EMPTY_CELL; } return ['s', v]; // `t="inlineStr"` means that `<is/>` holds the string value. // // Inside a `<c t="inlineStr"/>`, the specification requires there to exist an `<is/>` element, // and within that `<is/>` element it requires to exist a `<t/>` element. // // Example: // // <c r="A1" s="1" t="inlineStr"> // <is> // <t> // Test 123 // </t> // </is> // </c> // case 'inlineStr': if (inlineString === undefined) { return 'VALUE_MISSING'; } return ['s', inlineString]; // `type="s"` means that the string value is stored in the Shared Strings Table. // This way it attempts to compress the `.xlsx` file by reusing all string values // in case they repeat throughout the spreadsheet. // // This optimization can't be used when writing an `.xlsx` file in a "streaming" // fashion, i.e. when the entire spreadsheet data is not known in adavance // at the start of writing the file. // But it can be used in all other situations. And hence, it is used. // So this is the most common cell type, actually. // // Example: // // <c r="A3" t="s"> // <v>3</v> // </c> // case 's': // If a cell has no value then there's no `<c/>` element for it. // If a `<c/>` element exists then it's not empty. // The `<v/>` element's text is a zero-based index in the "shared strings" dictionary. if (!v) { return 'VALUE_MISSING'; } var sharedStringIndex = Number(v); // The shared string index value could be: // * not a number // * a number but a fractional one // * an integer that is out of bounds of the shared strings array if (isNaN(sharedStringIndex) || sharedStrings[sharedStringIndex] === undefined) { return 'VALUE_INVALID'; } return ['s', sharedStrings[sharedStringIndex]]; // Boolean (TRUE/FALSE) values are stored as either "1" or "0" in cells of type "b". // // Example: // // <c r="A1" t="b"> // <v>1</v> // </c> // case 'b': if (!v) { return 'VALUE_MISSING'; } if (v === '1') { return ['b', true]; } if (v === '0') { return ['b', false]; } return 'VALUE_INVALID'; // If cell type is "e", the `<v/>` element's text is an error code string (required). // // Example: // // <c r="A1" t="e"> // <f>1/0</f> // <v>#DIV/0!</v> // </c> // case 'e': // Examples of error codes: // // '#NULL!' // '#DIV/0!' // '#VALUE!' // '#REF!' // '#NAME!' // '#NUM!' // '#N/A' // '#SPILL!' // // The description of each error could be read in the formulas guide: // https://github.com/MiniMax-AI/skills/blob/main/skills/minimax-xlsx/references/validate.md // if (!v) { return 'VALUE_MISSING'; } return ['e', v]; // XLSX supports date cells of type "d", though it seems like it (almost?) never // uses type "d" for storing dates, preferring type "n" and numeric timestamp instead. // The value of a "d" cell is supposedly a string in "ISO 8601" format. // I haven't seen an `.xlsx` file having such cells. // // Example: // // <c r="A1" s="1" t="d"> // <v> // 2021-06-10T00:47:45.700Z // </v> // </c> // case 'd': if (!v) { return EMPTY_CELL; } var parsedDate = new Date(v); if (isNaN(parsedDate.valueOf())) { return 'VALUE_INVALID'; } return ['d', parsedDate.getTime()]; // type "n" is used for numeric cells. // // An optional `s` attribute defines how this number should be formatted —  // it should be a zero-based index of the style (XF record) in `styles.xml`. // // Example: // // <c r="A1" s="1" t="n"> // <v>123.45</v> // </c> // case 'n': // Numeric cell value is allowed to be missing in the XLSX specification // because "n" is the default type of a cell, so it ends up representing empty cells too. if (!v) { return EMPTY_CELL; } // XLSX does support `type: "d"` cells for storing dates, but it's not commonly used. // Instead, it prefers using `type: "n"` cells for storing dates as timestamps // with a corresponding `s` formatting style. if (s) { var styleId = Number(s); // The style ID value could be: // * not a number // * a number but a fractional one // * an integer that is out of bounds of the styles array if (isNaN(styleId) || styles[styleId] === undefined) { return 'FORMAT_INVALID'; } // Whether it's a date cell or just a numeric cell could only be determined // by looking at the formatting style. if ((0, _isDateFormatStyle["default"])(styles[styleId], defaultDateFormat, dateTemplateParser, dateFormatDetectionCache)) { var timestamp = Number(v); if (isNaN(timestamp)) { return 'VALUE_INVALID'; } // First, parse the "serial date" number from string. // Then convert the "serial date" number to a date timestamp. return ['d', (0, _parseExcelTimestamp["default"])(timestamp, epoch1904)]; } } // Return the cell value as a number, // unless custom `parseNumber()` function was passed, // in which case return the number as an unparsed string. // Some people prefer passing a custom `parseNumber()` function // to parse "big integers" without losing any precision. if (parseNumberCustom) { return ['n', v]; } var number = Number(v); if (isNaN(number)) { return 'VALUE_INVALID'; } return ['n', number]; default: return 'TYPE_INVALID'; } } //# sourceMappingURL=parseCell.js.map