Home Reference Source

src/file.js

import { Sheet } from './sheet';
import * as templates from './templates';
import { RefTable } from './reftable';
import { makeXworkbook, Xsheets, Xsheet, makeWorkbookRels } from './xmlWorkbook';
import { makeXTypes, XOverride } from './xmlContentTypes';
import { XstyleSheet } from './xmlStyle';
import Zip from 'jszip';

/**
 * This is the main class, use it:
 *
 * ```js
 * import { File } from 'better-xlsx';
 * const file = new File();
 * const sheet = file.addSheet('Sheet-1');
 * ```
 *
 * @class File
 */
export class File {
  /**
   * @private
   */
  sheet = {};
  /**
   * @private
   */
  sheets = [];
  /**
   * @private
   */
  definedNames = [];

  constructor () {
    /**
     * @private
     */
    this.styles = new XstyleSheet({});
  }
  /**
   * Add a new Sheet, with the provided name, to a File
   * @param {String} name Name of the Sheet
   * @return {Sheet}
   */
  addSheet (name) {
    if (this.sheet[name]) {
      throw new Error(`duplicate sheet name ${name}.`);
    }
    const sheet = new Sheet({
      name,
      file: this,
      selected: this.sheets.length === 0
    });
    this.sheet[name] = sheet;
    this.sheets.push(sheet);
    return sheet;
  }
  /**
   * Save the File to an xlsx file.
   * @param  {String} [type='nodebuffer'] For Node.js use `nodebuffer` and browser use `blob` or `base64`.
   * @param {Boolean} [compress=false] For file compression.
   * @return {Promise|stream} For Node.js return `stream` and browser return Promise.
   */
  saveAs (type = 'nodebuffer', compress = false) {
    const parts = this.makeParts();
    const zip = new Zip();
    for (const key of Object.keys(parts)) {
      zip.file(key, parts[key]);
    }
    const compression = compress ? 'DEFLATE' : 'STORE';
    if (type === 'blob' || type === 'base64') {
      return zip.generateAsync({ type, compression });
    } else {
      return zip.generateNodeStream({ type: 'nodebuffer', compression });
    }
  }
  /**
   * @private
   * @return {Object} XML files mapping object
   */
  makeParts () {
    const parts = {};
    const refTable = new RefTable();
    const types = makeXTypes();
    const workbook = makeXworkbook();

    this.styles.reset();

    let i = 1;
    const sheets = new Xsheets();
    for (const sheet of this.sheets) {
      const xSheet = sheet.makeXSheet(refTable, this.styles);
      types.children.push(new XOverride({
        PartName: `/xl/worksheets/sheet${i}.xml`,
        ContentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml'
      }));
      sheets.children.push(new Xsheet({
        name: sheet.name,
        sheetId: i,
        'r:id': `rId${i}`,
        state: 'visible'
      }));
      parts[`xl/worksheets/sheet${i}.xml`] = xSheet.render();
      i++;
    }
    workbook.sheets = sheets;

    parts['xl/workbook.xml'] = workbook.render();
    parts['_rels/.rels'] = templates.DOT_RELS;
    parts['docProps/app.xml'] = templates.DOCPROPS_APP;
    parts['docProps/core.xml'] = templates.DOCPROPS_CORE;
    parts['xl/theme/theme1.xml'] = templates.XL_THEME_THEME;

    parts['xl/sharedStrings.xml'] = refTable.makeXsst().render();
    parts['xl/_rels/workbook.xml.rels'] = makeWorkbookRels(this.sheets.length).render();
    parts['[Content_Types].xml'] = types.render();
    parts['xl/styles.xml'] = this.styles.render();

    return parts;
  }
}