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

xml-diff-kit

v0.1.1

Published

A TypeScript toolkit for parsing, normalizing, diffing, patching, and formatting XML document changes.

Readme

xml-diff-kit

English | 简体中文

一个 TypeScript XML 差异工具包,用于解析、标准化、比较、补丁应用、序列化和格式化 XML 文档变更。

xml-diff-kit 面向需要结构化、机器可读 XML 差异数据的场景,而不是面向可视化文本对比。它适合结构化编辑器、审阅流程、变更追踪、补丁应用、XML 文档比较,以及浏览器端 XML 工具。

它可以在 Node.js 和现代浏览器中使用。包同时提供 ESM 和 CJS 产物,公共 API 不依赖 Node.js 专属运行时能力。

安装

npm install xml-diff-kit

使用方式

下面大部分示例共用同一组 XML:

const oldXml = '<procedure><step id="s1">Remove the panel.</step></procedure>';
const newXml = '<procedure><step id="s1">Remove the access panel.</step><step id="s2">Inspect.</step></procedure>';

diffXml

比较两个 XML 文档,输出结构化 diff operations。

import { diffXml } from 'xml-diff-kit';

const ops = diffXml(oldXml, newXml, {
  keyAttrs: ['id'],
});

console.log(ops);

输出:

[
  {
    op: 'replaceText',
    path: '/procedure[0]/step[@id="s1"][0]/text()[0]',
    oldValue: 'Remove the panel.',
    newValue: 'Remove the access panel.',
    changes: [{ op: 'insertText', offset: 11, text: 'access ' }],
    segments: [
      { type: 'equal', text: 'Remove the ' },
      { type: 'insert', text: 'access ' },
      { type: 'equal', text: 'panel.' }
    ]
  },
  {
    op: 'addNode',
    path: '/procedure[0]/step[@id="s2"][1]',
    value: {
      type: 'element',
      name: 'step',
      namespaceURI: null,
      attrs: { id: 's2' },
      children: [{ type: 'text', text: 'Inspect.' }]
    }
  }
]

patchXml

把结构化 diff operations 应用回 XML 字符串或已解析的 XML 节点。

当你已经有一组 XmlDiffOp[],并希望把它们应用到 XML 文档上时,可以使用 patchXml。当输入是 XML 字符串时,patchXml 返回字符串。当输入是 XmlNode 时,它返回补丁后的 XmlNode

新增节点

import { patchXml, type XmlDiffOp } from 'xml-diff-kit';

const xml = '<root><a/></root>';

const ops: XmlDiffOp[] = [
  {
    op: 'addNode',
    path: '/root[0]/b[1]',
    value: {
      type: 'element',
      name: 'b',
      namespaceURI: null,
      attrs: {},
      children: []
    }
  }
];

const patchedXml = patchXml(xml, ops);

console.log(patchedXml);

输出:

<root><a/><b/></root>

更新属性

import { patchXml, type XmlDiffOp } from 'xml-diff-kit';

const xml = '<root status="draft"/>';

const ops: XmlDiffOp[] = [
  {
    op: 'updateAttr',
    path: '/root[0]',
    name: 'status',
    oldValue: 'draft',
    newValue: 'released'
  }
];

const patchedXml = patchXml(xml, ops);

console.log(patchedXml);

输出:

<root status="released"/>

替换文本

import { patchXml, type XmlDiffOp } from 'xml-diff-kit';

const xml = '<root>old text</root>';

const ops: XmlDiffOp[] = [
  {
    op: 'replaceText',
    path: '/root[0]/text()[0]',
    oldValue: 'old text',
    newValue: 'new text',
    changes: [
      {
        op: 'replaceTextRange',
        offset: 0,
        oldText: 'old',
        newText: 'new'
      }
    ],
    segments: [
      { type: 'delete', text: 'old' },
      { type: 'insert', text: 'new' },
      { type: 'equal', text: ' text' }
    ]
  }
];

const patchedXml = patchXml(xml, ops);

console.log(patchedXml);

输出:

<root>new text</root>

patchXml 根据路径应用变更。路径中的数字索引是实际用于定位节点的部分。类似 [@id="s1"] 的 key 提示用于提升可读性,并帮助 diffXml 对齐节点,但 patch 时仍然依赖数字索引。

支持的 patch 操作包括新增、删除、替换、移动节点;新增、更新、删除属性;以及替换文本节点内容。

formatDiff

把结构化 diff operations 格式化为摘要对象,或者 Markdown 报告。

import { diffXml, formatDiff } from 'xml-diff-kit';

const ops = diffXml(oldXml, newXml, { keyAttrs: ['id'] });
const summary = formatDiff(ops);

console.log(summary);

输出:

[
  {
    type: 'textChanged',
    path: '/procedure[0]/step[@id="s1"][0]/text()[0]',
    message: 'Changed text at /procedure[0]/step[@id="s1"][0]/text()[0]',
    before: 'Remove the panel.',
    after: 'Remove the access panel.'
  },
  {
    type: 'nodeAdded',
    path: '/procedure[0]/step[@id="s2"][1]',
    message: 'Added node at /procedure[0]/step[@id="s2"][1]',
    after: {
      type: 'element',
      name: 'step',
      namespaceURI: null,
      attrs: { id: 's2' },
      children: [{ type: 'text', text: 'Inspect.' }]
    }
  }
]

Markdown 输出:

const markdown = formatDiff(ops, { format: 'markdown' });

console.log(markdown);

输出:

# XML Diff

Total changes: 2

## 1. Changed text

- Path: `/procedure[0]/step[@id="s1"][0]/text()[0]`

**Before**

```text
Remove the panel.
```

**After**

```text
Remove the access panel.
```

**Text segments**

- equal: `Remove the `
- insert: `access `
- equal: `panel.`

## 2. Added node

- Path: `/procedure[0]/step[@id="s2"][1]`

```xml
<step id="s2">Inspect.</step>
```

parseXmlserializeXml

把 XML 解析为内部 AST,再序列化回 XML 字符串。

import { parseXml, serializeXml } from 'xml-diff-kit';

const doc = parseXml('<root><item id="1">Hello</item><item id="2">World</item></root>');
const xml = serializeXml(doc, { pretty: true });

console.log(xml);

输出:

<root>
  <item id="1">Hello</item>
  <item id="2">World</item>
</root>

normalizeXml

在 diff 或自定义处理前,标准化 XML AST。

import { normalizeXml, parseXml } from 'xml-diff-kit';

const doc = parseXml('<root b="2" a="1">  <item> value </item>  </root>');

const normalized = normalizeXml(doc, {
  ignoreWhitespaceText: true,
  trimText: true,
  sortAttributes: true,
});

console.log(normalized);

输出:

{
  type: 'element',
  name: 'root',
  namespaceURI: null,
  attrs: { a: '1', b: '2' },
  children: [
    {
      type: 'element',
      name: 'item',
      namespaceURI: null,
      attrs: {},
      children: [{ type: 'text', text: 'value' }]
    }
  ]
}

diffText

直接比较两个文本值。replaceText 操作内部使用的就是同一套文本 diff。

import { diffText } from 'xml-diff-kit';

const textDiff = diffText('Remove the panel.', 'Remove the access panel.');

console.log(textDiff);

输出:

{
  changes: [{ op: 'insertText', offset: 11, text: 'access ' }],
  segments: [
    { type: 'equal', text: 'Remove the ' },
    { type: 'insert', text: 'access ' },
    { type: 'equal', text: 'panel.' }
  ]
}

Diff 操作类型

支持的结构化 XML 变更:

  • addNode
  • removeNode
  • replaceNode
  • moveNode
  • replaceText
  • addAttr
  • updateAttr
  • removeAttr

文本变更会作为 replaceText 内部的 range 操作表达。

选项

interface XmlDiffOptions {
  ignoreWhitespaceText?: boolean;
  trimText?: boolean;
  ignoreComments?: boolean;
  sortAttributes?: boolean;
  keyAttrs?: string[];
  detectMoves?: boolean;
}

keyAttrs 用于让 diff 引擎通过稳定标识对齐兄弟元素,例如 idxml:id 或业务自定义 key。

detectMoves 是可选能力。启用后,带 key 的兄弟节点重排会被报告为 moveNode。默认关闭,以保持 patch 行为保守。

路径说明

Diff operations 使用从 XML 根节点开始的绝对路径:

/procedure[0]/step[@id="s1"][0]/text()[0]

数字索引是 patchXml 实际用于定位节点的部分。类似 [@id="s1"] 的 key 提示用于提升可读性,并辅助 keyed matching。

开发

npm install
npm run lint
npm run typecheck
npm test
npm run coverage
npm run build

发布

npm install
npm run lint
npm run typecheck
npm test
npm run build
npm publish --access public

License

MIT