npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pols-exceljs-plus

v2.4.0

Published

`pols-exceljs-plus` es una extensión de la biblioteca `exceljs` que proporciona utilidades avanzadas para agilizar la lectura, escritura y estructuración de datos en hojas de cálculo Excel.

Readme

pols-exceljs-plus

pols-exceljs-plus es una extensión de la biblioteca exceljs que proporciona utilidades avanzadas para agilizar la lectura, escritura y estructuración de datos en hojas de cálculo Excel.

Características

  • PXls Workbook: Extiende el Workbook nativo de exceljs inyectando métodos de ayuda en todas las hojas (Worksheet).
  • Lectura por Esquema Declarativo (getValuesBySchema): Extrae y valida filas o columnas a partir de un esquema en forma de objeto, con conversión automática de tipos, validación de obligatoriedad, valores por defecto y parseadores personalizados.
  • Lectura de Tablas Dinámicas (getTableValues): Extrae una tabla completa a partir de una fila de cabeceras, asociando dinámicamente columnas por nombre (soporta RegExp), acumulando múltiples coincidencias con parse y detectando el fin de la tabla automáticamente.
  • Lectura de Tablas con Cabecera Jerárquica (getHierarchicalTableValues): Extrae una tabla completa cuya cabecera ocupa varias filas (con celdas combinadas formando niveles), calculando por columna una "ruta" jerárquica (ej. "Principal > Secundario") y mapeándola a las propiedades del objeto resultante mediante path (string, string[] o RegExp); sin esquema, trae todas las columnas detectadas. Devuelve { data, structure }, donde structure describe cómo se resolvió cada columna del esquema contra el archivo (exists, duplicated), y transform recibe un preview encadenado para columnas duplicadas.
  • Escritura simplificada: Métodos setValues, setRowValues y setColumnValues para escribir arreglos bidimensionales y unidimensionales aplicando estilos y fusiones de celdas fácilmente.
  • Lectura rápida de valores (getValue): Recuperación de valores de celdas con formateo de fechas, resolución de fórmulas y extracción de hipervínculos automáticos.

Instalación

npm install pols-exceljs-plus

Uso

Inicializar PXls

Para utilizar las utilidades extendidas, utiliza la clase PXls en lugar del Workbook nativo de exceljs:

import { PXls } from 'pols-exceljs-plus';

const workbook = new PXls();
await workbook.readFile('mi_archivo.xlsx');

// Obtener hoja (ya viene decorada con todos los métodos de utilidad)
const sheet = workbook.getWorksheet('Hoja1');

Lectura por Esquema con getValuesBySchema

Este método permite extraer y formatear los valores de una fila o columna según un esquema definido por un objeto. El orden de las propiedades del objeto determina por defecto el orden secuencial de lectura.

Firma del método:

sheet.getValuesBySchema(schema, readMode, row, column)
  • schema: Objeto declarativo que define la forma de la respuesta esperada.
  • readMode: 'row' para leer horizontalmente (columnas consecutivas) o 'column' para leer verticalmente (filas consecutivas).
  • row: Fila de inicio (1-indexed).
  • column: Columna de inicio (1-indexed).

Opciones del Esquema

Cada propiedad del esquema puede ser:

  1. Un Constructor / Tipo abreviado: String, Number, Boolean, Date, 'string', 'number', 'boolean', 'date', o 'any'.
  2. Un Objeto de Configuración Completo:
    • type: Tipo de dato o constructor.
    • cellIndex (opcional): Desplazamiento relativo explícito desde la celda inicial (permite saltarse celdas o leer en desorden).
    • parse (opcional): Función callback para transformar/limpiar el valor obtenido: (value: any) => any.

Ejemplo de Lectura:

// Supongamos que en la fila 5 de Excel tenemos:
// Col 1 (A): 456
// Col 2 (B): "María Pérez"
// Col 3 (C): 2026-06-24 (Fecha)
// Col 4 (D): (Celda vacía)
// Col 5 (E): "  texto con espacios  "

const esquemaCliente = {
  id: Number,                                                   // Convierte a número secuencialmente (Col A)
  nombre: String,                                               // Convierte a string (Col B)
  fechaRegistro: 'date',                                        // Convierte a objeto Date (Col C)
  estado: { type: String, cellIndex: 3 },                       // Lee Col D (vacía) y devuelve null
  observaciones: { 
    parse: (val) => typeof val === 'string' ? val.trim() : val,
    cellIndex: 4                                                // Lee Col E de forma explícita
  }
};

const cliente = sheet.getValuesBySchema(esquemaCliente, 'row', 5, 1);
console.log(cliente);
/*
Output:
{
  id: 456,
  nombre: "María Pérez",
  fechaRegistro: Date("2026-06-24..."),
  estado: null,
  observaciones: "texto con espacios"
}
*/

Lectura de Tablas con getTableValues

Este método permite leer una tabla completa a partir de una fila de cabeceras. Busca dinámicamente las columnas basándose en sus nombres de cabecera definidos en el esquema (soporta coincidencia por texto exacto o expresión regular RegExp).

Firma del método:

sheet.getTableValues(schema, row, column)
  • schema: Objeto que define las propiedades y cómo encontrarlas/formatearlas. Cada propiedad debe ser un objeto con los siguientes atributos:
    • headerName: Cadena de texto (string) o expresión regular (RegExp) para identificar la cabecera de la columna correspondiente.
    • type (opcional): Constructor o tipo de conversión (String, Number, Boolean, Date, 'string', 'number', 'boolean', 'date', o 'any').
    • parse (opcional): Función callback para transformar el valor: (value: any, prevValue?: any) => any.
  • row: Fila donde se encuentra la cabecera (1-indexed).
  • column: Columna inicial desde donde se empezará a buscar las cabeceras de forma horizontal (1-indexed).

Características particulares:

  1. Detección de Cabeceras: El método recorre horizontalmente la fila de partida (row) desde la columna inicial (column) hasta toparse con una celda vacía (lo cual da por terminado el escaneo de cabeceras).
  2. Detección de Fin de Tabla: Lee hacia abajo fila por fila. Para optimizar el rendimiento, sólo lee las celdas cuyas cabeceras coinciden con algún headerName. Detiene su lectura cuando se topa con una fila completamente vacía en todas las columnas identificadas.
  3. Múltiples Cabeceras para una propiedad:
    • Si el headerName coincide con más de una columna (por ejemplo /(base imponible)|impuesto/), por defecto se tomará el valor de la última columna procesada (de izquierda a derecha).
    • Si se define la función parse, esta se invocará secuencialmente recibiendo como primer parámetro el valor de la columna actual y como segundo parámetro el valor acumulado de las columnas coincidentes previas, permitiendo realizar agregaciones (por ejemplo, sumas o concatenaciones).

Ejemplo de Lectura de Tabla:

// Supongamos que en la fila 2 de Excel tenemos las cabeceras:
// B2: "Nombres", C2: "Edades", D2: "Monto 1", E2: "Monto 2"
// Y las filas siguientes contienen la información

const schema = {
  nombre: {
    type: 'string',
    headerName: /Nombres/
  },
  montoTotal: {
    type: 'number',
    headerName: /Monto/,
    parse: (val, prevVal) => (prevVal || 0) + (val || 0) // Suma las columnas que coincidan con "Monto"
  }
};

const datos = sheet.getTableValues(schema, 2, 2);
console.log(datos);
/*
Output:
[
  { nombre: "Juan", montoTotal: 150 },
  { nombre: "Maria", montoTotal: 350 }
]
*/

Lectura de Tablas con Cabecera Jerárquica con getHierarchicalTableValues

Este método permite leer una tabla completa cuya cabecera ocupa varias filas, típico de reportes donde una columna "padre" agrupa a varias columnas "hijas" mediante celdas combinadas (merge). Por cada columna de datos, el método arma una ruta jerárquica concatenando el valor de cada fila de la cabecera (por ejemplo "Principal > Secundario"), y el esquema asocia esa ruta exacta a la propiedad final del objeto devuelto.

A diferencia de getTableValues (que usa un objeto { clave: { headerName, ... } } y permite que TypeScript infiera el tipo de retorno a partir del propio esquema), getHierarchicalTableValues recibe el esquema como un arreglo y expone un único parámetro de tipo genérico <T> con la forma del objeto que se desea recibir, ya que la ruta jerárquica no siempre es un identificador práctico para inferir tipos automáticamente.

Firma del método:

sheet.getHierarchicalTableValues<T>({ schema, headerRows, r, c, separator? })

Todos los parámetros se reciben en un único objeto:

  • schema (opcional, admite null): Arreglo de definiciones de columna. Si se omite o se pasa null, no se filtra ni renombra nada: el objeto resultante incluye una propiedad por cada ruta jerárquica distinta detectada en la cabecera, usando esa ruta tal cual como nombre de propiedad (equivale a un esquema automático { path: <ruta> } por cada columna encontrada). Cuando se define, cada elemento del arreglo es un objeto con:
    • path (string | string[] | RegExp): Cómo identificar la(s) columna(s) a asociar:
      • string: coincide por igualdad exacta con la ruta jerárquica calculada (ej. "Principal > Secundario").
      • string[]: coincide con cualquier columna cuya ruta esté incluida en el arreglo.
      • RegExp: coincide con cualquier columna cuya ruta calculada matchee la expresión regular.
    • to (opcional, string): Nombre de la propiedad en el objeto resultante. Si se omite y path es un string, se usa el propio path como nombre de propiedad. Es obligatorio cuando path es un string[] o un RegExp (no hay una ruta única de la cual derivar un nombre); si se omite en esos casos, el método lanza un error.
    • trimed (opcional, boolean, por defecto true): Controla cómo se lee cada celda de la cabecera antes de compararla con path. Con true (el valor por defecto), a cada segmento de la ruta se le recorta el espacio sobrante al inicio/fin y, si queda un apóstrofe inicial (el que Excel usa para forzar que un número se trate como texto), también se elimina. Con false, se compara contra el texto tal como quedó leído (sin quitar ese apóstrofe inicial).
    • transform (opcional): Función callback para transformar el valor encontrado: (value: any, preview?: any) => unknown. Se llama una vez por cada columna del archivo que coincide con el path (ver más abajo qué pasa cuando hay varias columnas coincidentes). El segundo parámetro, preview, recibe el valor devuelto por la llamada anterior de transform para esa misma fila; en la primera llamada de cada fila, preview es undefined.
  • headerRows: Cantidad de filas que ocupa el bloque de cabecera (number).
  • r: Fila donde comienza la cabecera (1-indexed).
  • c: Columna inicial desde donde se empezará a buscar las cabeceras de forma horizontal (1-indexed).
  • separator (opcional): Separador usado para unir los niveles de la ruta jerárquica. Por defecto es " > ".

Cómo se arma la ruta jerárquica de cada columna

  1. Para cada columna, se recorren sus celdas de cabecera fila por fila (desde r hasta r + headerRows - 1).
  2. Celdas combinadas horizontalmente: si la celda de cabecera está fusionada con una celda de una columna anterior en la misma fila, se toma el valor de la celda "maestra" del merge. Esto hace que, por ejemplo, si B1:C1 están combinadas con el texto "Principal", tanto la columna B como la columna C usen "Principal" como primer nivel de su ruta, sin necesidad de repetir el texto en cada celda.
  3. Celdas combinadas verticalmente: si la celda de una fila de cabecera es la misma celda fusionada que la de la fila anterior (dentro del mismo bloque de cabecera), ese nivel no se repite en la ruta. Así, una columna "ID" cuya celda de cabecera ocupa las 2 filas del bloque produce la ruta "ID", no "ID > ID".
  4. Los niveles vacíos se omiten (no generan separadores colgando en la ruta).
  5. Por cada segmento se calculan dos variantes: una "trimmed" (espacios recortados y sin el apóstrofe inicial de escape) y una "cruda" (tal como se leyó). Cuál de las dos se usa para comparar contra path depende de la propiedad trimed de cada item del esquema (ver arriba); sin schema, siempre se usa la variante "trimmed".
  6. El escaneo de columnas se detiene apenas la celda de la primera fila de la cabecera está vacía para esa columna (igual que getTableValues).

Detección de fin de tabla

Igual que getTableValues: lee hacia abajo fila por fila a partir de r + headerRows, sólo mirando las columnas que hicieron match con algún path del esquema, y se detiene al encontrar una fila completamente vacía en esas columnas.

Forma del resultado

A diferencia de getTableValues, el método devuelve un objeto con dos propiedades:

  • data (T[]): Un objeto por cada fila de datos, igual que antes.
  • structure (PHierarchicalSchemaResult): Un arreglo que describe, columna por columna, cómo se resolvió el esquema contra la cabecera real del archivo, en el orden en que las columnas aparecen en el archivo (de izquierda a derecha). Cada elemento es el item del esquema original (path, to, label, transform) más dos propiedades adicionales:
    • exists (boolean): Si se definió un schema, indica si esa columna fue declarada en él. false significa que el item se declaró en el schema pero no se encontró ninguna columna en el archivo que matcheara su path; estos items se agregan al final de structure (no respetan el orden de aparición porque no aparecieron). Sin schema (autogenerado), siempre es true.
    • duplicated (boolean): Indica que el path de ese item coincidió con más de una columna del archivo (cabeceras repetidas, o un path de tipo string[]/RegExp que matchea varias columnas). La primera columna encontrada para ese item tiene duplicated: false; cualquier columna adicional que matchee el mismo item tiene duplicated: true.
const { structure, data } = sheet.getHierarchicalTableValues<Fila>({ schema, headerRows: 2, r: 1, c: 2 });

Columnas duplicadas y encadenamiento de transform con preview

Cuando el path de un item coincide con varias columnas del archivo (cabeceras repetidas, o un path de tipo string[]/RegExp que matchea más de una columna), transform se invoca una vez por cada columna coincidente, en el orden en que aparecen en el archivo (de izquierda a derecha). El valor final asignado a la fila es el que devuelve la última llamada.

En cada llamada, el segundo parámetro (preview) recibe el valor devuelto por la llamada anterior para esa misma fila (o undefined en la primera columna). Esto permite, por ejemplo, acumular valores de columnas duplicadas:

// Cabecera con "Monto" repetido en las columnas B, C y D (valores 10, 20 y 30)
const schema = [
  {
    path: 'Monto',
    to: 'total',
    transform: (value: number, preview?: number) => (preview ?? 0) + value,
  },
];

const { data } = sheet.getHierarchicalTableValues({ schema, headerRows: 1, r: 1, c: 2 });
// data[0] === { total: 60 }  // 10 + 20 + 30, encadenado vía `preview`

Ejemplo de Lectura:

// Cabecera en las filas 1 y 2, empezando en la columna B:
//
//        B                C              D
//   1 |  Principal (B1:C1 combinadas) |  ID (D1:D2 combinadas)
//   2 |  Secundario  |  Otra prop     |
//
// Datos a partir de la fila 3:
//   B3: "Juan"   C3: 25   D3: 1001
//   B4: "Maria"  C4: 30   D4: 1002

type Fila = { nombre: string; edad: number; id: string };

const schema = [
  { path: 'Principal > Secundario', to: 'nombre' },
  { path: 'Principal > Otra prop', to: 'edad', transform: (val: any) => Number(val) },
  { path: 'ID', to: 'id', transform: (val: any) => `ID-${val}` },
];

const { data } = sheet.getHierarchicalTableValues<Fila>({ schema, headerRows: 2, r: 1, c: 2 });
console.log(data);
/*
Output:
[
  { nombre: "Juan", edad: 25, id: "ID-1001" },
  { nombre: "Maria", edad: 30, id: "ID-1002" }
]
*/

Sin schema: traer todo lo que se encuentre

Si no interesa mapear a nombres de propiedad específicos, se puede omitir schema (o pasar null) para obtener un objeto por fila con todas las columnas detectadas, usando la ruta jerárquica calculada como nombre de propiedad:

const { data } = sheet.getHierarchicalTableValues({ headerRows: 2, r: 1, c: 2 });
console.log(data);
/*
Output:
[
  { 'Principal > Secundario': 'Juan', 'Principal > Otra prop': 25, 'ID': 1001 },
  { 'Principal > Secundario': 'Maria', 'Principal > Otra prop': 30, 'ID': 1002 }
]
*/

path como string[] o RegExp: asociar varias columnas a una misma propiedad

const schema = [
  // Coincide con cualquiera de las dos rutas exactas listadas
  { path: ['Principal > Secundario', 'Principal > Otra prop'], to: 'cualquieraDePrincipal' },

  // Coincide con cualquier columna cuya ruta empiece con "Principal >"
  { path: /^Principal >/, to: 'cualquieraPorRegex' },
];

const { data, structure } = sheet.getHierarchicalTableValues({ schema, headerRows: 2, r: 1, c: 2 });

Cuando un path (string[] o RegExp) matchea más de una columna, el valor final en data es el de la última columna coincidente (de izquierda a derecha) — salvo que se use transform, en cuyo caso se encadena vía preview (ver más arriba). En structure, cada columna coincidente aparece como una entrada independiente: la primera con duplicated: false, las siguientes con duplicated: true.

Notas

  • Si ninguna columna coincide con un path del esquema, la propiedad correspondiente queda en null en cada fila (y si ningún path del esquema encuentra columna, la tabla se considera vacía y data es [] de inmediato).
  • Si dos o más columnas terminan generando la misma ruta jerárquica (cabeceras duplicadas), o si un path de tipo string[]/RegExp matchea varias columnas, gana el valor de la última columna coincidente (de izquierda a derecha) en data — salvo que se use transform, que se encadena vía preview; en structure, cada columna coincidente genera su propia entrada marcada con duplicated.
  • to es obligatorio cuando path es un string[] o un RegExp: al no haber una única ruta de la cual derivar el nombre de propiedad, omitirlo hace que el método lance un error.

Escritura Simplificada

Escribe matrices o arreglos lineales de manera ágil usando los métodos de escritura extendida:

// Escribir múltiples filas y columnas de una sola vez
sheet.setValues(1, 1, [
  [1, "Producto A", 19.99],
  [2, "Producto B", 25.50]
]);

// Escribir una sola fila con un estilo por defecto
sheet.setRowValues(3, 1, ["ID", "Descripción", "Precio"], {
  backgroundColor: "E0E0E0",
  color: "000000",
  span: 1
});

// Escribir una columna
sheet.setColumnValues(4, 1, [100, 200, 300]);

Lectura Rápida de Celdas

// Obtiene el valor parseado automáticamente resolviendo fechas, fórmulas e hipervínculos
const valor = sheet.getValue<string>(1, 2);

Licencia

Este proyecto está bajo la licencia ISC.