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

@senlinz/import-export

v1.1.0

Published

import/export excel core

Readme

@senlinz/import-export

面向浏览器的高阶 Excel 模板生成、导出与导入 API。

English

安装

pnpm add @senlinz/import-export

WASM 加载

  • importExcelimportExcelDynamicexportExcelfromExcelfromExcelDynamictoExceldownloadExcelTemplategenerateExcelTemplate 会在首次调用时自动加载并初始化内置 WASM 资源。
  • 在 Vite 和其他浏览器 ESM bundler 中,使用高阶 core 包时不再需要手动调用 initializeWasm(...) 或自己处理 ?url
  • 如果你需要自行管理 WASM 模块,请直接使用 @senlinz/import-export-wasm,而不是这个高阶包。
import { toExcel } from '@senlinz/import-export';

const workbook = await toExcel(
  {
    name: 'TomAndJerry',
    columns: [{ key: 'name', name: 'Name', dataType: 'text' }],
  },
  [{ name: 'Tom' }],
);

支持的 API

错误消息多语言

为了兼容现有使用方式,错误消息默认仍为英文。在 ExcelDefinitionDynamicExcelImportOptions 中设置 locale: 'zh'locale: 'zh-CN' 后,会使用内置中文错误消息。

你也可以按稳定错误码覆盖消息,支持字符串模板或函数。函数会收到错误码、规范化后的语言、默认消息、原始 cause 以及结构化参数。

import { fromExcel, ImportExportError } from '@senlinz/import-export';

const definition = {
  name: 'LocalizedImport',
  locale: 'zh-CN',
  columns: [{ key: 'name', name: '姓名', dataType: 'text' }],
  errorMessages: {
    HEADER_MISMATCH: ({ params }) => `表头错误:${params.cell} 需要 ${params.expected},实际是 ${params.actual}`,
    INVALID_DATA_TYPE: '列 {columnKey} 的类型 {dataType} 不支持,可选值:{supportedDataTypes}',
  },
};

try {
  await fromExcel(definition, workbookBytes);
} catch (error) {
  if (error instanceof ImportExportError) {
    console.log(error.code, error.params, error.message);
  }
}

包内导出 ImportExportErrorValidationErrorImportErrorExportErrorWasmInitError。每个错误都包含 codeparamslocalecause

稳定的 definition 字段

  • name:模板 / 导出下载时使用的文件名
  • sheetName:模板 / 导出时使用的工作表名称;schema 导入会读取第一个工作表
  • columns:表头、校验和行映射使用的稳定列定义
  • author:可选的工作簿作者元数据
  • createTime:可选的工作簿创建时间,支持 Date 或日期字符串
  • maxFileSizeBytes:浏览器上传的字节大小上限(默认 25 MiB),在读取前先校验
  • titletitleHeighttitleFormat:可选的合并标题行及其高度、样式
  • defaultRowHeightheaderRowHeight:导出时数据行和表头行的行高
  • dxdy:表头写入前的横向、纵向偏移量
  • isHeaderFreeze:冻结表头区域,滚动时仍保持列标题可见
  • progressCallback:长时间导入 / 导出过程中的进度回调
  • imageFetcher:导出 image 列时必需的图片数据解析回调
  • escapeFormulas:导出时自动转义类似公式的文本,默认开启,可显式关闭以允许公式
  • locale:内置错误消息语言(enzhzh-CN,默认 en
  • errorMessages / messages:按稳定错误码自定义错误消息

稳定的列字段

  • key:唯一的程序字段名
  • name:工作表中显示的列表头
  • width:导出列宽
  • note:附加在表头单元格上的备注 / 注释
  • dataType:标量单元格类型
  • allowedValues:单元格允许值的校验列表
  • parent:多级表头时的父级表头 key
  • format:列的基础单元格格式
  • valueFormat:导出值的直接或条件格式覆盖
  • dataGroup:嵌套导出数据使用的逻辑分组标识
  • dataGroupParent:嵌套导出数据使用的父级分组标识

支持的 dataType

  • text:默认文本类型
  • number:有限数值
  • date:导出时支持 Date 实例或日期字符串
  • image:通过 imageFetcher 解析图片 URL 或标识

导入 / 导出流程

import { downloadExcelTemplate, exportExcel, importExcel } from '@senlinz/import-export';

const definition = {
  name: 'TomAndJerry',
  sheetName: 'sheet1',
  columns: [
    { key: 'name', name: 'Name', dataType: 'text', width: 20, note: 'required' },
    { key: 'age', name: 'Age', dataType: 'number', width: 10 },
    { key: 'birthday', name: 'Birthday', dataType: 'date', width: 18 },
    { key: 'category', name: 'Category', allowedValues: ['Cat', 'Mouse'] },
    { key: 'image', name: 'Image', dataType: 'image', width: 10 },
  ],
  imageFetcher: async (url: string) => new Uint8Array(await (await fetch(url)).arrayBuffer()),
};

await downloadExcelTemplate(definition);

await exportExcel(definition, [
  { name: 'Tom', age: 12, birthday: '2024-11-01 00:00:00', category: 'Cat', image: '/Tom.jpg' },
  { name: 'Jerry', age: null, birthday: null, category: 'Mouse', image: '/Jerry.png' },
]);

const rows = await importExcel(definition);

动态导入

当你不想预先定义 columns,并希望直接使用浏览器文件选择框时,可使用 importExcelDynamic(options?)

import { importExcelDynamic } from '@senlinz/import-export';

const result = await importExcelDynamic({
  sheetName: 'sheet1',
  headerRow: 1,
});

当你已经拿到工作簿字节数据时,可继续使用 fromExcelDynamic(buffer, options?)

import { fromExcelDynamic } from '@senlinz/import-export';

const result = await fromExcelDynamic(fileBytes, {
  sheetName: 'sheet1',
  headerRow: 1,
});
  • importExcelDynamic(...) 依赖 DOM / 浏览器文件上传 API。
  • sheetName 可选,未提供或不存在时会回退到第一个工作表。
  • headerRow 可选,使用从 1 开始的行号,默认会自动选择首个非空行。
  • maxFileSizeBytes 可选,与 schema 导入相同,默认 25 MiB。
  • localeerrorMessages 可用于自定义动态导入错误。
  • 返回值结构为 { sheetName, headers, rows }
  • 动态导入要求选中的表头行中表头名称非空且唯一。

导入行为

  • 表头必须与 columns[].name 完全一致。
  • Schema 导入会读取工作簿中的第一个工作表。
  • number 列导入后返回数字。
  • 空的 number / date 单元格导入后返回 null
  • date 列导入后返回格式化字符串。
  • 非法或不完整的 schema 会在读取工作簿前立即失败。
  • 浏览器上传会在读取前按 maxFileSizeBytes 校验文件大小(默认 25 MiB)。
  • 无 schema 导入时,可使用 importExcelDynamic(...)(浏览器上传)或 fromExcelDynamic(...)(字节缓冲)。

导出行为

  • nullundefined 会导出为空单元格。
  • 数字列会拒绝非有限值。
  • 日期列支持 Date 实例或日期字符串。
  • 文本列默认会转义看起来像公式的值;如需导出公式,可设置 escapeFormulas: false
  • 分组导出对象必须使用 { value?, children: [...] } 结构。
  • 图片列必须提供 imageFetcher

带 URL 校验的 imageFetcher

image 列使用 imageFetcher 时,应先校验 URL 再发起请求,避免访问不可信来源。下面的示例只允许 HTTPS,并检查 HTTP 响应状态:

const imageFetcher = async (url: string): Promise<Uint8Array> => {
  const parsed = new URL(url);
  if (parsed.protocol !== 'https:') {
    throw new Error(`Refusing to fetch image from non-HTTPS URL: ${url}`);
  }
  const response = await fetch(url);
  if (!response.ok) {
    throw new Error(`Failed to fetch image: ${response.status} ${response.statusText}`);
  }
  const buffer = await response.arrayBuffer();
  return new Uint8Array(buffer);
};

在服务端场景中,还应限制可信域名,并拒绝私有或内网地址。完整的图片拉取信任模型和额外建议见 SECURITY.md

已支持的高级能力

以下高级能力已纳入公开 API:

  • 合并标题行
  • 冻结表头
  • 列备注
  • 下拉候选值
  • 条件 valueFormat
  • 使用 dataGroup / dataGroupParent 的嵌套分组导出
  • 通过 imageFetcher 导出图片

浏览器 / 运行时支持

  • 主要面向浏览器 ESM 运行时。
  • downloadExcelTemplateexportExcelimportExcelimportExcelDynamic 依赖 DOM / 浏览器 API。
  • 当运行时提供兼容的浏览器全局对象时,也可以使用 fromExcelfromExcelDynamictoExcelgenerateExcelTemplate
  • core 包会在首次执行工作簿操作前,异步初始化构建产物中的内置 WASM 资源。

示例

这些示例由 ./tests/import-export.spec.ts 中的 Playwright 用例覆盖。

测试工具

包内导出 testUtils 命名空间,供消费者测试套件使用:

import { testUtils } from '@senlinz/import-export';

警告: testUtils 仅用于测试。这些 helper 是内部实现细节,不提供稳定性保证,可能在任意版本中变更或移除。

| Helper | 说明 | | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------- | | normalizeDefinition(definition) | 规范化并校验 ExcelDefinition,包括裁剪字符串和应用默认值。 | | normalizeDynamicImportOptions(options?) | 规范化并校验动态导入选项,例如 headerRowmaxFileSizeBytes。 | | sanitizeTextCellValue(value, escapeFormulas?) | 清洗文本单元格值以避免公式注入;当 escapeFormulastrue(默认)时,会给类似公式的值加单引号前缀。 | | defaultMaxFileSizeBytes | 默认最大文件大小,单位为字节(25 MB)。 |

已知限制

  • 父级表头和数据分组必须先于依赖它们的列声明。
  • date 导入结果会规范化为字符串,而不是 Date 实例。
  • 当前一次只校验一个工作表。